原文地址: Java 并发编程 ② - 线程生命周期与状态流转转载请注明出处!
前言
往期文章:
继上一篇结尾讲的,这一篇文章主要是讲线程的生命周期以及状态流转。主要内容有:
- Java 中对线程状态的定义,与操作系统线程状态的对比
- 线程状态的流转图
- 如何自己验证状态的流转
一、Java 线程的状态
先来谈一谈Java 中线程的状态。在 java.lang.Thread.State
类是 Thread
的内部枚举类,在里面定义了Java 线程的六个状态,-注释信息也非常的详细。
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:
*
* - {@link Object#wait() Object.wait} with no timeout
* - {@link #join() Thread.join} with no timeout
* - {@link LockSupport#park() LockSupport.park}
*
*
* A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* 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:
*
* - {@link #sleep Thread.sleep}
* - {@link Object#wait(long) Object.wait} with timeout
* - {@link #join(long) Thread.join} with timeout
* - {@link LockSupport#parkNanos LockSupport.parkNanos}
* - {@link LockSupport#parkUntil LockSupport.parkUntil}
*
*
* 超时等待态,在调用了以下方法后会进入超时等待状态
* 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 操作系统中的线程状态
再来看,操作系统层面,线程存在五类状态,状态的流转关系可以参考下面的这张图。
可以看到,Java 中所说的线程状态和操作系统层面的线程状态是不太一样的。
- Java 中的
RUNNABLE
其实包含了OS中的RUNNING
和READY
- Java 中的
WAITING
、TIMED_WAITING
、BLOCKED
其实是对OS中WAITING
状态的一个更细致的划分
在Thread.State
源码中也写了这么一句话:
These states are virtual machine states which do not reflect any operating system thread states.这些状态只是线程在虚拟机中的状态,并不反映操作系统的线程状态。
对于这两个层面对比,你需要知道的是,Java的线程状态是服务于虚拟机的。从这个角度来考虑的话,把底层OS中的RUNNING
和READY
状态映射上来也没多大意义,因此,统一成为RUNNABLE
状态是不错的选择,而对WAITING
状态更细致的划分,也是出于这么一个考虑。
二、状态流转图
图很详细,结合前面的内容一起食用。
关于阻塞状态,这里还要多说几句话,我们上面说的,都是在JVM 代码层面的实际线程状态。但是在一些书比如《码出高效》中,会把Java 线程的阻塞状态分为:
- 同步阻塞:即锁被其他线程占用
- 主动阻塞:指调用了Thread 的某些方法,主动让出CPU执行权,比如sleep()、join()等
- 等待阻塞:执行了wait()系列方法
三、测试
这里演示一下,如何在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 的用法和原理,感兴趣请持续关注。
如果本文有帮助到你,希望能点个赞,这是对我的最大动力????。