关于多线程:多线程1线程基础

46次阅读

共计 23842 个字符,预计需要花费 60 分钟才能阅读完成。

如何创立线程

Java 中,创立线程的话,个别有两种形式

  • 继承 Thread 类
  • 实现 Runnable 接口

继承 Thread 类

    public static void main(String[] args) {System.out.println("主线程 ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();}
public class MyThread extends Thread {
    private String name;

    public MyThread(String name) {this.name = name;}

    @Override
    public void run() {System.out.println("name:" + name + "子线程 ID:" + Thread.currentThread().getId());
    }
}

后果

主线程 ID:1
name:thread2 子线程 ID:1
name:thread1 子线程 ID:12

start():

先来看看 Java API 中对于该办法的介绍:使该线程开始执行;Java 虚拟机调用该线程的 run 办法。后果是两个线程并发地运行;以后线程(从调用返回给 start 办法)和另一个线程(执行其 run 办法)。屡次启动一个线程是非法的。特地是当线程曾经完结执行后,不能再重新启动,用 start 办法来启动线程,真正实现了多线程运行,这时无需期待 run 办法体中的代码执行结束而间接继续执行后续的代码。通过调用 Thread 类的 start()办法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦失去 cpu 工夫片,就开始执行 run()办法,这里的 run()办法 称为线程体,它蕴含了要执行的这个线程的内容,Run 办法运行完结,此线程随即终止。

run():

同样先看看 Java API 中对该办法的介绍:如果该线程是应用独立的 Runnable 运行对象结构的,则调用该 Runnable 对象的 run 办法;否则,该办法不执行任何操作并返回。 Thread 的子类应该重写该办法。run()办法只是类的一个一般办法而已,如果间接调用 Run 办法,程序中仍然只有主线程这一个线程,其程序执行门路还是只有一条,还是要程序执行,还是要期待 run 办法体执行结束后才可继续执行上面的代码,这样就没有达到写线程的目标。

论断

依据以上信息能够得出结论

thread1 和 thread2 的线程 ID 不同,thread2 和主线程 ID 雷同,阐明调用 start 办法方可启动新的线程,而 run 办法只是 thread 类中的一个一般办法调用,还是在主线程里执行,并不会创立新的线程

尽管 thread1 的 start 办法调用在 thread2 的 run 办法后面调用,然而先输入的是 thread2 的 run 办法调用的相干信息,阐明新线程创立的过程不会阻塞主线程的后续执行

实现 Runnable 接口

public class Main {public static void main(String[] args) {System.out.println("主线程 ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();}
}
public class MyRunnable implements Runnable {public MyRunnable() { }

    @Override
    public void run() {System.out.println("子线程 ID:" + Thread.currentThread().getId());
    }
}

后果

主线程 ID:1
子线程 ID:12

通过实现 Runnable 接口,咱们定义了一个子工作,而后将子工作交给 Thread 去执行,这种形式须要将 Runnable 作为 Thread 类的参数,而后通过 Thread 的 start 办法来创立一个新的线程来执行子工作,如果调用 Runnable 的 run 办法就不会创立线程,和一般办法没有区别,实际上 Thread 类就是实现了 Runnable 接口。

public class Thread implements Runnable {

在 Java 中,两种形式都能够用来创立线程去执行子工作,然而 Java 只容许单继承,所以如果自定义类须要继承其余类,就只能实现 Runnable 接口。

Thread 类的应用

首先咱们来介绍下线程的状态

线程状态从大的方面来说,可归结为:初始 / 新建状态(new)、可运行 / 就绪状态(runnable)、运行状态(running)、阻塞状态(blocked、time waiting、waiting)、沦亡状态(dead)五个状态:

新建状态(New):

​ 当用 new 操作符创立一个线程时,例如 new Thread(r),线程还没有开始运行,此时线程处在新建状态。当一个线程处于新生状态时,程序还没有开始运行线程中的代码

就绪状态(Runnable):

一个新创建的线程并不主动开始运行,要执行线程,必须调用线程的 start()办法。当线程对象调用 start()办法即启动了线程,start()办法创立线程运行的系统资源,并调度线程运行 run()办法。当 start()办法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立刻运行 run()办法,线程还必须同其余线程竞争 CPU 工夫,只有取得 CPU 工夫才能够运行线程。因为在单 CPU 的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因而此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由 Java 运行时零碎的线程调度程序 (thread scheduler) 来调度的。

运行状态(Running)

当线程取得 *CPU* 工夫后,它才进入运行状态,真正开始执行 run()办法.

阻塞状态(Blocked):

线程运行过程中,可能因为各种起因进入阻塞状态:

  • 线程通过调用 sleep 办法进入睡眠状态;
  • 线程调用一个在 I / O 上被阻塞的操作,即该操作在输入输出操作实现之前不会返回到它的调用者;
  • 线程试图失去一个锁,而该锁正被其余线程持有;
  • 线程在期待某个触发条件;
    所谓阻塞状态是正在运行的线程没有运行完结,临时让出 CPU,这时其余处于就绪状态的线程就能够取得 CPU 工夫,进入运行状态。

死亡状态(Dead):

有两个起因会导致线程死亡:
1) run 办法失常退出而天然死亡,
2) 一个未捕捉的异样终止了 run 办法而使线程猝死。
为了确定线程在以后是否存活着(就是要么是可运行的,要么是被阻塞了),须要应用 isAlive 办法。如果是可运行或被阻塞,这个办法返回 true;如果线程仍旧是 new 状态且不是可运行的,或者线程死亡了,则返回 false.

上下文切换

线程上下文是指某一时间点 CPU 寄存器和程序计数器的内容,CPU 通过工夫片调配算法来循环执行工作(线程),因为工夫片十分短,所以 CPU 通过不停地切换线程执行。

CPU 在一个时刻只能运行一个线程,当在运行一个线程的过程直达去运行另外一个线程,这个叫做线程上下文切换,这样就须要记录不同线程的运行状态,以便下次从新切换回来时可能持续切换到之前的状态运行,上下文的切换实际上就是存储和复原 CPU 状态的过程,使 CPU 能够从端点继续执行。

多线程能够使工作执行效率得以晋升,然而线程切换一样有代价,开发中要衡量过后再设计方案,例如 Redis 就为了疾速而应用单线程

Thread 类的办法

start()

start()用来启动一个线程,当调用 start 办法后,零碎才会开启一个新的线程来执行用户定义的子工作,在这个过程中,会为相应的线程调配须要的资源。

    
    /* The group of this thread 线程组        ThreadGroup 是 Java 提供的一种对线程进行分组治理的伎俩,能够对所有线程以组为单位进行操作,为线程服务,用户通过应用线程组的概念批量治理线程,如批量进行或挂起、设置优先级、守护线程等
    */
    private ThreadGroup group;
    
    /**
     * Causes this thread to begin execution; the Java Virtual Machine    线程开始执行
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the        一个线程应用 start 办法、另一个应用 run 办法,的后果是两个线程同时运行
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.    一个线程启动两次是不非法的
     * In particular, a thread may not be restarted once it has completed    一个线程在执行实现后可能无奈启动
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already    线程曾经存在异样
     * synchronized 办法,保障在同一时刻,只有一个线程能够执行某个办法或某个代码块,同时 synchronized 能够保障一个线程的变动可见(可见性)*               started.
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         * 如果线程状态不为 NEW,就抛出异样
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started    告诉线程组,这个线程将要被启动,以便于增加到线程组列表中,并缩小该线程组未开始计数
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {start0();
            // 标记线程曾经启动
            started = true;
        } finally {
            try {if (!started) {
                    // 启动失败,线程组设置线程启动失败
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    /**
     * native 关键字阐明其润饰的办法是一个原生态办法,办法对应的实现不是在以后文件,而是在用其余语言(如 C 和 C ++)实现的文件中。Java 语言自身不能对操作系统底层进行拜访和操作,然而能够通过 JNI 接口调用其余语言来实现对底层的拜访
     * 这个关键字示意调用本机的操作系统函数,因为多线程须要底层操作系统的反对
     */
    private native void start0();

run()

run()办法是不须要用户来调用的,当通过 start 办法启动一个线程之后,当线程取得了 CPU 执行工夫,便进入 run 办法体去执行具体的工作。留神,继承 Thread 类必须重写 run 办法,在 run 办法中定义具体要执行的工作。

/**
 * If this thread was constructed using a separate    如果这个线程应用独自的结构 Runnable 运行对象,那么会调用 Runnable 对象的 run 办法,否则什么都不会返回
 * <code>Runnable</code> run object, then that
 * <code>Runnable</code> object's <code>run</code> method is called;
 * otherwise, this method does nothing and returns.
 * <p>
 * Subclasses of <code>Thread</code> should override this method.    应用 Thread 应该重写此办法
 *
 * @see     #start()
 * @see     #stop()
 * @see     #Thread(ThreadGroup, Runnable, String)
 */
@Override
public void run() {if (target != null) {target.run();
    }
}

sleep()

sleep 办法有两个重载版本:

    /**
     * Causes the currently executing thread to sleep (temporarily cease    导致以后正在执行的线程进入睡眠状态(临时进行执行)以指定的毫秒数为准,基于零碎计时器和调度程序的精度和准确性,不会失去监视器所有权
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds plus the specified
     * number of nanoseconds, subject to the precision and accuracy of system
     * timers and schedulers. The thread does not lose ownership of any
     * monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @param  nanos    额定的纳秒睡眠
     *         {@code 0-999999} additional nanoseconds to sleep
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative, or the value of
     *          {@code nanos} is not in the range {@code 0-999999}
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static void sleep(long millis, int nanos)
    throws InterruptedException {if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {millis++;}

        sleep(millis);
    }

sleep 办法不会开释锁,也就是说如果以后线程持有对某个对象的锁,则即便调用 sleep 办法,其余线程也无法访问这个对象

public class Main {
    private int i = 10;
    private Object object = new Object();

    public static void main(String[] args) {Main main = new Main();
        MyThread thread1 = main.new MyThread();
        MyThread thread2 = main.new MyThread();
        thread1.start();
        thread2.start();}

    class MyThread extends Thread{
        @Override
        public void run() {synchronized (object) {
                i++;
                System.out.println("i:"+i);
                try {System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
                    Thread.currentThread().sleep(5000);
                } catch (InterruptedException e) {// TODO: handle exception}
                System.out.println("线程"+Thread.currentThread().getName()+"睡眠完结");
                i++;
                System.out.println("i:"+i);
            }
        }
    }
}
i:11
线程 Thread- 0 进入睡眠状态
线程 Thread- 0 睡眠完结
i:12
i:13
线程 Thread- 1 进入睡眠状态
线程 Thread- 1 睡眠完结
i:14

从下面输入后果能够看出,当 Thread- 0 进入睡眠状态之后,Thread- 1 并没有去执行具体的工作。只有当 Thread- 0 执行完之后,此时 Thread- 0 开释了对象锁,Thread- 1 才开始执行。

留神,如果调用了 sleep 办法,必须捕捉 InterruptedException 异样或者将该异样向下层抛出。当线程睡眠工夫满后,不肯定会立刻失去执行,因为此时可能 CPU 正在执行其余的工作。所以说调用 sleep 办法相当于让线程进入阻塞状态

语法方面:
sleep 办法的签名外面有 InterruptedException 这个 checked exception,所以调用方必须捕捉或者再次抛出。

线程的中断单干机制方面:
对于 java 的多线程来说,调用一个能够被阻塞的办法(wait,sleep,join 等等),意味着代码到这行会被阻塞,那么中断阻塞的时候应该怎么办?
一般来说,是采纳 InterruptedException 配合 Interrupt 状态来单干实现的。
如果你调用的办法是个会阻塞的办法(个别会抛出 InterruptedException),通用的做法是:
1,如果你的办法签名能够蕴含 InterruptedException,那么间接抛出,让你的办法也变成一个阻塞办法。
2,如果你的办法签名不容许你减少 InterruptedException,那么须要你在捕捉 InterruptedException 后及时重置 Interrupt 状态(个别你调用的阻塞函数在抛出 InterruptedException 后会革除 Interrupt 状态)。
以上 2 种手法的目标都是为了下层调用方能晓得本线程被中断了,而后始终将这个 Interrupt 状态传递到线程的管理者模块那,由他再决定如何解决这个线程。

所以你不肯定须要捕捉,依据你的办法的签名,决定采纳计划 1 或者 2 来解决这个异样。

yield()

调用 yield 办法会让以后线程交出 CPU 权限,让 CPU 去执行其余的线程。它跟 sleep 办法相似,同样不会开释锁。然而 yield 不能管制具体的交出 CPU 的工夫,另外,yield 办法只能让领有雷同优先级的线程有获取 CPU 执行工夫的机会。留神,调用 yield 办法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只须要期待从新获取 CPU 执行工夫,这一点是和 sleep 办法不一样的。

    /**
     * A hint to the scheduler that the current thread is willing to yield    给调度程序的提醒以后线程违心放弃对处理器的以后应用。调度程序能够随便疏忽此提醒
     * its current use of a processor. The scheduler is free to ignore this 
     * hint.
     *
     * <p> Yield is a heuristic attempt to improve relative progression         Yield 是一种启发式尝试,旨在进步线程之间的绝对过程,否则将适度利用 CPU。应将其应用与具体的性能剖析和基准测试联合起来,以确保它实际上具备所需的成果。* between threads that would otherwise over-utilise a CPU. Its use
     * should be combined with detailed profiling and benchmarking to
     * ensure that it actually has the desired effect.
     *
     * <p> It is rarely appropriate to use this method. It may be useful    很少适宜应用此办法。它可能对调试或测试有用,因为它可能有助于重现因为竞争条件而产生的谬误。当设计诸如 {@link java.util.concurrent.locks} 包中的并发
     * for debugging or testing purposes, where it may help to reproduce    控制结构时,它也可能很有用。* bugs due to race conditions. It may also be useful when designing
     * concurrency control constructs such as the ones in the
     * {@link java.util.concurrent.locks} package.
     */
    public static native void yield();

join()

如果在 main 线程中,调用 thread.join 办法,则 main 办法会期待 thread 线程执行结束或者期待肯定的工夫。如果调用的是无参 join 办法,则期待 thread 执行结束,如果调用的是指定了工夫参数的 join 办法,则期待肯定的事件。

    /**
     * Waits for this thread to die.    期待线程死亡
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {join(0);
    }    


    /**
     * Waits at most {@code millis} milliseconds for this thread to    期待线程死亡,设置等待时间下限
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls    此实现应用以 {@code this.isAlive} 为条件的 {@code this.wait} 调用循环。线程终止时,将调用 {@code this.notifyAll} 办法。倡议应用程序不要在{@code Thread}
     * conditioned on {@code this.isAlive}. As a thread terminates the    实例上应用 {@code wait},{@ code notify} 或{@code notifyAll}。* {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final synchronized void join(long millis)
    throws InterruptedException {long base = System.currentTimeMillis();
        long now = 0;
       
        if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {while (isAlive()) {wait(0);
            }
        } else {while (isAlive()) {
                // 线程须要存活状态能力持续期待,否则完结,等到已等待时间大于或等于 millis,跳出循环
                long delay = millis - now;
                // 如果等待时间完结
                if (delay <= 0) {break;}
                // 调用 wait 办法,让线程进入阻塞状态,并开释线程占有的锁,交出 CPU 执行权限,sleep 则不会开释锁
                wait(delay);
                // 已等待时间
                now = System.currentTimeMillis() - base;}
        }
    }

    /**
     * Waits at most {@code millis} milliseconds plus
     * {@code nanos} nanoseconds for this thread to die.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @param  nanos
     *         {@code 0-999999} additional nanoseconds to wait
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative, or the value
     *          of {@code nanos} is not in the range {@code 0-999999}
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final synchronized void join(long millis, int nanos)
    throws InterruptedException {if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {millis++;}

        join(millis);
    }
public class JoinMain {public static void main(String[] args) throws IOException {System.out.println("进入线程" + Thread.currentThread().getName());
        JoinMain joinMain = new JoinMain();
        MyThread thread1 = joinMain.new MyThread();
        thread1.start();
        try {System.out.println("线程" + Thread.currentThread().getName() + "期待");
            // 期待线程死亡
            thread1.join();
            System.out.println("线程" + Thread.currentThread().getName() + "继续执行");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();}
    }

    class MyThread extends Thread {
        @Override
        public void run() {System.out.println("进入线程" + Thread.currentThread().getName());
            try {Thread.currentThread().sleep(5000);
            } catch (InterruptedException e) {// TODO: handle exception}
            System.out.println("线程" + Thread.currentThread().getName() + "执行结束");
        }
    }
}

依据打印内容,咱们能够看到,线程 thread1 执行后,直到执行结束才开始继续执行 main 线程,因为有 wait 办法,所有 join 同样也会让线程开释对一个对象持有的锁。

进入线程 main
线程 main 期待
进入线程 Thread-0
线程 Thread- 0 执行结束
线程 main 继续执行

interrupt()

interrupt,顾名思义,即中断的意思。独自调用 interrupt 办法能够使得处于阻塞状态的线程抛出一个异样,也就说,它能够用来中断一个正处于阻塞状态的线程;另外,通过 interrupt 办法和 isInterrupted()办法来进行正在运行的线程

    /**
     * Interrupts this thread.
     *
     * <p> Unless the current thread is interrupting itself, which is    中断此线程。<p> 除非以后线程始终在中断本身(总是容许这样做),否则将调用此线程的 {@link #checkAccess()checkAccess} 办法,这可能会引发{@link 
     * always permitted, the {@link #checkAccess() checkAccess} method    SecurityException}。* of this thread is invoked, which may cause a {@link
     * SecurityException} to be thrown.
     *
     * <p> If this thread is blocked in an invocation of the {@link                <p> 如果在调用 {@link Object#wait()wait()},{@ link Object#wait(long)wait(long)} 或{@link Object#wait(long){@link Object}类或
     * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link    {@link #join()},{@ link #join(long)},{@ link #join(long,int)},{@ link #sleep(long)}或 {@link #sleep(long,int)} 这类办法,则
     * Object#wait(long, int) wait(long, int)} methods of the {@link Object}    其中断状态将被革除,并且将收到{@link InterruptedException}。* class, or of the {@link #join()}, {@link #join(long)}, {@link
     * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     *
     * <p> If this thread is blocked in an I/O operation upon an {@link            <p> 如果此线程在 {@link java.nio.channels.InterruptibleChannel InterruptibleChannel} 上的 I / O 操作中被阻塞,则该通道将被敞开,该线程的中断状
     * java.nio.channels.InterruptibleChannel InterruptibleChannel}                态将被设置,并且该线程将收到一个{@link java.nio.channels.ClosedByInterruptException}。* then the channel will be closed, the thread's interrupt
     * status will be set, and the thread will receive a {@link
     * java.nio.channels.ClosedByInterruptException}.
     *
     * <p> If this thread is blocked in a {@link java.nio.channels.Selector}    <p> 如果此线程在 {@link java.nio.channels.Selector} 中被阻塞,则将设置该线程的中断状态,并且它将立刻从抉择操作中返回,可能具备非零值,就像选择器的
     * then the thread's interrupt status will be set and it will return        {@link java.nio.channels.Selector#wakeup 唤醒}办法已被调用。* immediately from the selection operation, possibly with a non-zero
     * value, just as if the selector's {@link
     * java.nio.channels.Selector#wakeup wakeup} method were invoked.
     *
     * <p> If none of the previous conditions hold then this thread's interrupt        <p> 如果没有以上条件,则将设置该线程的中断状态。</ p>
     * status will be set. </p>
     *
     * <p> Interrupting a thread that is not alive need not have any effect.        <p> 中断未运行的线程不会产生任何成果。*
     * @throws  SecurityException
     *          if the current thread cannot modify this thread                        如果以后线程无奈批改此线程,则 @throws SecurityException
     *
     * @revised 6.0
     * @spec JSR-51
     */
    public void interrupt() {if (this != Thread.currentThread())
            checkAccess();
        
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                //native 办法
                interrupt0();           // Just to set the interrupt flag
                // 设置中断标记位
                b.interrupt(this);
                return;
            }
        }
        interrupt0();}

    /**
     * Tests whether the current thread has been interrupted.  The                测试以后线程是否已被中断。通过此办法能够革除线程的 <i> 中断状态 </ i>。换句话说,如果要间断两次调用此办法,则第二次调用将返回 false(除非以后线程
     * <i>interrupted status</i> of the thread is cleared by this method.  In    在第一次调用革除其中断状态之后且在第二次调用查看其状态之前再次中断)。p> 因为以后中断已被中断 <code> false </ @返回 <code> true </ code>,该办法
     * other words, if this method were to be called twice in succession, the    将返回 false,从而疏忽因为线程中断而导致线程中断的疏忽。代码 > 否则。@ see #isInterrupted()@订正 6.0
     * second call would return false (unless the current thread were
     * interrupted again, after the first call had cleared its interrupted
     * status and before the second call had examined it).
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if the current thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see #isInterrupted()
     * @revised 6.0
     */
    public static boolean interrupted() {return currentThread().isInterrupted(true);
    }

    /**
     * Tests whether this thread has been interrupted.  The <i>interrupted    测试此线程是否已被中断。该办法不影响线程的 <i> 中断状态 </ i>。<p> 因为该办法返回 false 将反映线程中断,因为该线程在中断时未处于活动状态。@ return < 
     * status</i> of the thread is unaffected by this method.    code> true </ code>,如果此线程已被中断 <code> false </ code>,否则。@ see #interrupted()@revised 6.0
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if this thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see     #interrupted()
     * @revised 6.0
     */
    public boolean isInterrupted() {return isInterrupted(false);
    }
public class InterruptMain {public static void main(String[] args) throws IOException {InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) { }
        thread.interrupt();}

    class MyThread extends Thread {
        @Override
        public void run() {
            try {System.out.println("进入睡眠状态");
                Thread.currentThread().sleep(10000);
                System.out.println("睡眠结束");
            } catch (InterruptedException e) {System.out.println("失去中断异样");
            }
            System.out.println("run 办法执行结束");
        }
    }
}

从这里能够看出,通过 interrupt 办法能够中断处于阻塞状态的线程。

进入睡眠状态
失去中断异样
run 办法执行结束

接下来看一下通过 interrupt 办法是否能够中断非阻塞状态的线程。

public class InterruptMain {public static void main(String[] args) throws IOException {InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) { }
        thread.interrupt();}

    class MyThread extends Thread{
        @Override
        public void run() {
            int i = 0;
            while(i<Integer.MAX_VALUE){System.out.println(i+"while 循环");
                i++;
            }
        }
    }
}

从后果看出,while 循环会始终运行直到变量 i 的值超出 Integer.MAX_VALUE。所以说间接调用 interrupt 办法不能中断正在运行中的线程。

9766171 while 循环
9766172 while 循环
9766173 while 循环
9766174 while 循环
9766175 while 循环

尽管间接调用 interrupt 办法不能中断正在运行中的线程,然而能够应用 isInterrupted()判断中断标记是否被置位来中断线程的执行。

public class InterruptMain {public static void main(String[] args) throws IOException {InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {//Thread.currentThread().sleep(2000);
            Thread.sleep(2000);
        } catch (InterruptedException e) { }
        thread.interrupt();}

    class MyThread extends Thread {
        @Override
        public void run() {
            int i = 0;
            while (!isInterrupted() && i < Integer.MAX_VALUE) {System.out.println(i + "while 循环");
                i++;
            }
        }
    }
}

从后果看出,打印若干个值之后,while 循环就进行打印了。

然而个别状况下不倡议通过这种形式来中断线程,个别会在 MyThread 类中减少一个属性 isStop 来标记是否完结 while 循环,而后再在 while 循环中判断 isStop 的值

416317 while 循环
416318 while 循环
416319 while 循环
416320 while 循环
416321 while 循环
416322 while 循环

这样就能够在里面通过调用 setStop 办法来终止 while 循环

class MyThread extends Thread{
        private volatile boolean isStop = false;
        @Override
        public void run() {
            int i = 0;
            while(!isStop){i++;}
        }
         
        public void setStop(boolean stop){this.isStop = stop;}
    }

stop()

stop 办法曾经是一个废除的办法,它是一个不平安的办法。因为调用 stop 办法会间接终止 run 办法的调用,并且会抛出一个 ThreadDeath 谬误,如果线程持有某个对象锁的话,会齐全开释锁,导致对象状态不统一。所以 stop 办法根本是不会被用到的。

destroy()

已废除

对于线程属性的办法

1)getId

用来失去线程 ID

    /**
     * Returns the identifier of this Thread.  The thread ID is a positive    返回此线程的标识符。线程 ID 是创立该线程时生成的正 <tt> long </ tt> 号。* <tt>long</tt> number generated when this thread was created.            线程 ID 是惟一的,并且在其生命周期内放弃不变。当线程终止时,能够从新应用该线程 ID。* The thread ID is unique and remains unchanged during its lifetime.
     * When a thread is terminated, this thread ID may be reused.
     *
     * @return this thread's ID.
     * @since 1.5
     */
    public long getId() {return tid;}

2)getName 和 setName

用来失去或者设置线程名称。

    /**
     * Changes the name of this thread to be equal to the argument    将该线程的名称更改为等于参数 <code> name </ code>。<p>
     * <code>name</code>.                                
     * <p>
     * First the <code>checkAccess</code> method of this thread is called    首先,不带任何参数调用此线程的 <code> checkAccess </ code> 办法。这可能导致抛出 <code> SecurityException </ code>。@ param name 这个线程的新名称。* with no arguments. This may result in throwing a                       @exception SecurityException 如果以后线程无奈批改此异样
     * <code>SecurityException</code>.
     *
     * @param      name   the new name for this thread.
     * @exception  SecurityException  if the current thread cannot modify this
     *               thread.
     * @see        #getName
     * @see        #checkAccess()
     */
    public final synchronized void setName(String name) {checkAccess();
        if (name == null) {throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {setNativeName(name);
        }
    }

    /**
     * Returns this thread's name.    返回线程的名称
     *
     * @return  this thread's name.
     * @see     #setName(String)
     */
    public final String getName() {return name;}

3)getPriority 和 setPriority

用来获取和设置线程优先级。

    /**
     * Changes the priority of this thread.                                更改此线程的优先级。<p>
     * <p>
     * First the <code>checkAccess</code> method of this thread is called    首先,不带任何参数调用此线程的 <code> checkAccess </ code> 办法。这可能导致抛出 <code> SecurityException </ code>。* with no arguments. This may result in throwing a
     * <code>SecurityException</code>.
     * <p>
     * Otherwise, the priority of this thread is set to the smaller of        否则,此线程的优先级将设置为指定的 <code> newPriority </ code> 和该线程的线程组的最大容许优先级中的较小者。* the specified <code>newPriority</code> and the maximum permitted
     * priority of the thread's thread group.
     *
     * @param newPriority priority to set this thread to                    参数 newPriority 优先级将此线程设置为 @exception IllegalArgumentException 如果优先级不在范畴 <code> MIN_PRIORITY </ code> 至
     * @exception  IllegalArgumentException  If the priority is not in the      <code> MAX_PRIORITY </ code>。*               range <code>MIN_PRIORITY</code> to
     *               <code>MAX_PRIORITY</code>.
     * @exception  SecurityException  if the current thread cannot modify     如果以后线程无奈批改此线程报 SecurityException。*               this thread.
     * @see        #getPriority
     * @see        #checkAccess()
     * @see        #getThreadGroup()
     * @see        #MAX_PRIORITY
     * @see        #MIN_PRIORITY
     * @see        ThreadGroup#getMaxPriority()
     */
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {if (newPriority > g.getMaxPriority()) {newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    /**
     * Returns this thread's priority.    获取属性
     *
     * @return  this thread's priority.
     * @see     #setPriority
     */
    public final int getPriority() {return priority;}

4)setDaemon 和 isDaemon

用来设置线程是否成为守护线程和判断线程是否是守护线程。

守护线程和用户线程的区别在于:守护线程依赖于创立它的线程,而用户线程则不依赖。举个简略的例子:如果在 main 线程中创立了一个守护线程,当 main 办法运行结束之后,守护线程也会随着沦亡。而用户线程则不会,用户线程会始终运行直到其运行结束。在 JVM 中,像垃圾收集器线程就是守护线程。

    /**
     * Marks this thread as either a {@linkplain #isDaemon daemon} thread    将此线程标记为 {@linkplain #isDaemon 守护程序} 线程或用户线程。当所有正在运行的线程都是守护程序线程时,Java 虚拟机将退出。* or a user thread. The Java Virtual Machine exits when the only
     * threads running are all daemon threads.
     *
     * <p> This method must be invoked before the thread is started.    必须在启动线程之前调用此办法。*
     * @param  on
     *         if {@code true}, marks this thread as a daemon thread    如果 {@code true} 将此线程标记为守护线程
     *
     * @throws  IllegalThreadStateException                              如果此线程是{@linkplain #isAlive alive},则 @@抛出 IllegalThreadStateException
     *          if this thread is {@linkplain #isAlive alive}
     *
     * @throws  SecurityException
     *          if {@link #checkAccess} determines that the current        如果 {@link #checkAccess} 确定以后线程无奈批改该线程,则 @throws 抛出 SecurityException
     *          thread cannot modify this thread
     */
    public final void setDaemon(boolean on) {checkAccess();
        if (isAlive()) {throw new IllegalThreadStateException();
        }
        daemon = on;
    }

    /**
     * Tests if this thread is a daemon thread.                        测试此线程是否是守护程序线程
     *
     * @return  <code>true</code> if this thread is a daemon thread;
     *          <code>false</code> otherwise.
     * @see     #setDaemon(boolean)
     */
    public final boolean isDaemon() {return daemon;}

对于用户线程和守护线程

在 java 多线程开发中,有两类线程,别离是 User Thread(用户线程)和 Daemon Thread(守护线程)。

用户线程很好了解,咱们日常开发中编写的业务逻辑代码,运行起来都是一个个用户线程。而守护线程相对来说则要特地了解一下。

守护线程,相似于操作系统外面是守护过程。因为 Java 语言机制是构建在 JVM 的根底之上,这一机制意味着 Java 平台是把操作系统的过程给屏蔽了。所以须要在 JVM 外面结构出对本人无利的机制,于是守护线程应运而生。

所谓的守护线程,指的是程序运行时在后盾提供的一种通用服务的线程。比方垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的局部。因而,当所有的非守护线程完结时,程序也就终止了,同时会杀死过程中的所有守护线程。反过来说,只有任何非守护线程还在运行,程序就不会终止。

事实上,User Thread(用户线程)和 Daemon Thread(守护线程)从实质上来说并没有什么区别,惟一的不同之处就在于虚拟机的来到:如果用户线程曾经全副退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有持续运行程序的必要了。

守护线程并非只有虚拟机外部能够提供,用户也能够手动将一个用户线程设定 / 转换为守护线程。

在 Thread 类中提供了一个 setDaemon(true)办法来将一个一般的线程(用户线程)设置为守护线程。

正文完
 0