Java 并发编程 ② - 线程生命周期与状态流转

原文地址: Java 并发编程 ② - 线程生命周期与状态流转

转载请注明出处!java

前言

往期文章:编程

继上一篇结尾讲的,这一篇文章主要是讲线程的生命周期以及状态流转。主要内容有:并发

  • Java 中对线程状态的定义,与操做系统线程状态的对比
  • 线程状态的流转图
  • 如何本身验证状态的流转

1、Java 线程的状态

先来谈一谈Java 中线程的状态。在 java.lang.Thread.State 类是 Thread的内部枚举类,在里面定义了Java 线程的六个状态,-注释信息也很是的详细。post

public enum State {
        
        /**
         * Thread state for a thread which has not yet started.
         * 初始态,表明线程刚建立出来,可是尚未 start 的状态
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         * 
         * 运行态,表明线程正在运行或者等待操做系统资源,如CPU资源
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         * 
         * 阻塞态,表明线程正在等待一个监视器锁(即咱们常说的synchronized)
         * 或者是在调用了Object.wait以后被notify()从新进入synchronized代码块
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         * 
         * 等待态,调用如下方法会进入等待状态:
         * 1. 调用不会超时的Object.wait()方法
         * 2. 调用不会超时的Thread.join()方法
         * 3. 调用不会超时的LockSupport.park()方法
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         * 
         * 超时等待态,在调用了如下方法后会进入超时等待状态
         * 1. Thread.sleep()方法后
         * 2. Object.wait(timeout)方法
         * 3. Thread.join(timeout)方法
         * 4. LockSupport.parkNanos(nanos)方法
         * 5. LockSupport.parkUntil(deadline)方法
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         * 
         * 终止态,表明线程已经执行完毕
         */
        TERMINATED;
    }

关于上面JDK源码中对于BLOCKED状态的注释,这里有一点须要补充的,就是若是是线程调用了Object.wait(timeout)方法进入TIMED_WAITING状态以后,若是是由于超过指定时间,脱离TIMED_WAITING状态,若是接下去线程是要从新进入synchronize 代码块的话,也是会先进入等待队列,变成BLOCKED状态,而后请求监视器锁资源。测试

1.1 操做系统中的线程状态

再来看,操做系统层面,线程存在五类状态,状态的流转关系能够参考下面的这张图。spa

能够看到,Java 中所说的线程状态和操做系统层面的线程状态是不太同样的。操作系统

  • Java 中的 RUNNABLE 其实包含了OS中的RUNNINGREADY
  • Java 中的WAITINGTIMED_WAITINGBLOCKED实际上是对OS中WAITING状态的一个更细致的划分

Thread.State源码中也写了这么一句话:线程

These states are virtual machine states which do not reflect any operating system thread states.

这些状态只是线程在虚拟机中的状态,并不反映操做系统的线程状态code

对于这两个层面对比,你须要知道的是,Java的线程状态是服务于虚拟机的。从这个角度来考虑的话,把底层OS中的RUNNINGREADY状态映射上来也没多大意义,所以,统一成为RUNNABLE 状态是不错的选择,而对WAITING状态更细致的划分,也是出于这么一个考虑。orm

2、状态流转图

图很详细,结合前面的内容一块儿食用。

关于阻塞状态,这里还要多说几句话,咱们上面说的,都是在JVM 代码层面的实际线程状态。可是在一些书好比《码出高效》中,会把Java 线程的阻塞状态分为:

  • 同步阻塞:即锁被其余线程占用
  • 主动阻塞:指调用了Thread 的某些方法,主动让出CPU执行权,好比sleep()、join()等
  • 等待阻塞:执行了wait()系列方法

3、测试

这里演示一下,如何在IDEA 上面来验证上述的状态流转。有疑问或者有兴趣的读者能够按照一样的方法来验证。

我这里想要用代码验证下面的状况,

就是若是是线程1调用了 Object.wait(timeout)方法进入 TIMED_WAITING状态以后,若是是由于超过指定时间,脱离 TIMED_WAITING状态,若是接下去线程是要从新进入 synchronize 代码块的话,也是会先进入等待队列,变成 BLOCKED状态,而后请求监视器锁资源。
public class ThreadLifeTempTest {

    public static void main(String[] args) {
        Object object = new Object();

        new Thread(()->{
            synchronized (object) {
                try {
                    System.out.println("thread1 waiting");
                    // 等待10s,进入Timed_Waiting
                    // 10s 后会进入Blocked,获取object的监视器锁
                    object.wait(10000);
                    System.out.println("thread1 after waiting");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread1").start();

        new Thread(()->{
            synchronized (object) {
                try {
                    // sleep也不会释放锁,因此thread1 不会获取到锁
                    Thread.sleep(10000000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread2").start();
    }
}

使用IDEA的RUN模式运行代码,而后点击左边的一个摄像头按钮(dump thread),查看各线程的状态。

在Thread 1 等待 10s中时,dump的结果:Thread 1和Thread 2都处于 TIMED_WAITING状态,

"Thread2" #13 prio=5 os_prio=0 tid=0x0000000020196800 nid=0x65b8 waiting on condition [0x0000000020afe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$1(ThreadLifeTempTest.java:33)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$2/1096979270.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Thread1" #12 prio=5 os_prio=0 tid=0x0000000020190800 nid=0x25fc in Object.wait() [0x00000000209ff000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$0(ThreadLifeTempTest.java:21)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$1/1324119927.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

在Thread 1 等待 10s以后,Thread 1从新进入synchronize 代码块,进入等待队列,变成BLOCKED状态

"Thread2" #13 prio=5 os_prio=0 tid=0x0000000020196800 nid=0x65b8 waiting on condition [0x0000000020afe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$1(ThreadLifeTempTest.java:33)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$2/1096979270.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Thread1" #12 prio=5 os_prio=0 tid=0x0000000020190800 nid=0x25fc waiting for monitor entry [0x00000000209ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$0(ThreadLifeTempTest.java:21)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$1/1324119927.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

小结

在本篇文章中,主要讲解了线程的生命周期,各个状态以及状态流转。若是对线程状态的变化还有不了解的,能够借助最后一部分的测试方法来实际验证,帮助理解。

下一章,内容是介绍ThreadLocal 和 InheritableThreadLocal 的用法和原理,感兴趣请持续关注。

若是本文有帮助到你,但愿能点个赞,这是对个人最大动力🤝🤝🤗🤗。

参考

相关文章
相关标签/搜索