五种状态
这是从操作系统层面来描述的。
新建状态
:仅是在语言层面创建了线程对象,还未与操作系统线程关联可运行状态(就绪状态)
:指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行运行状态
:指获取了 CPU 时间片运行中的状态 当 CPU 时间片用完,会从运行状态转换至可运行状态,会导致线程的上下文切换阻塞状态
:如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入阻塞状态等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态。与可运行状态的区别是,对阻塞状态的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们终止状态(死亡状态)
:表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态
六种状态
这是从 Java API 层面来描述的 根据 Thread.State 枚举,分为六种状态
NEW
线程刚被创建,但是还没有调用 start() 方法RUNNABLE
是调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为 是可运行)BLOCKED
,WAITING
,TIMED_WAITING
都是 Java API 层面对【阻塞状态】的细分TERMINATED
当线程代码运行结束
例如:
@Slf4j(topic = "c.TestState")
public class TestState {
public static void main(String[] args) throws IOException {
// New
Thread t1 = new Thread("t1") {
@Override
public void run() {
log.debug("running...");
}
};
// Runnable
Thread t2 = new Thread("t2") {
@Override
public void run() {
while(true) {
}
}
};
t2.start();
// Terminated
Thread t3 = new Thread("t3") {
@Override
public void run() {
log.debug("running...");
}
};
t3.start();
// Timed_waiting
Thread t4 = new Thread("t4") {
@Override
public void run() {
// 这里t4先对对象上锁,t6线程就没办法加锁了,会陷入 blocked 状态
synchronized (TestState.class) {
try {
Thread.sleep(1000000); // timed_waiting
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t4.start();
// Waiting
Thread t5 = new Thread("t5") {
@Override
public void run() {
try {
t2.join(); // waiting
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t5.start();
// Blocked
Thread t6 = new Thread("t6") {
@Override
public void run() {
synchronized (TestState.class) { // blocked
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t6.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1 state {}", t1.getState());
log.debug("t2 state {}", t2.getState());
log.debug("t3 state {}", t3.getState());
log.debug("t4 state {}", t4.getState());
log.debug("t5 state {}", t5.getState());
log.debug("t6 state {}", t6.getState());
System.in.read();
}
}
/*Output:
18:02:11.284 c.TestState [t3] - running...
18:02:11.784 c.TestState [main] - t1 state NEW
18:02:11.784 c.TestState [main] - t2 state RUNNABLE
18:02:11.784 c.TestState [main] - t3 state TERMINATED
18:02:11.784 c.TestState [main] - t4 state TIMED_WAITING
18:02:11.784 c.TestState [main] - t5 state WAITING
18:02:11.784 c.TestState [main] - t6 state BLOCKED
*/
六种线程状态转换
有一点需要注意,在IDEA中调试时,调试台中
RUNNABLE
显示为RUNNING
,BLOCKED
显示为MONITOR
。
还是上面那幅图,其中在转换线上标了1-10,即代表10种转换情况。假设有线程 Thread t
情况 1 NEW --> RUNNABLE
- 当调用
t.start()
方法时,由NEW --> RUNNABLE
情况 2 RUNNABLE <--> WAITING
t线程用 synchronized(obj)
获取了对象锁后
- 调用
obj.wait()
方法时,t 线程从RUNNABLE --> WAITING
- 调用
obj.notify()
,obj.notifyAll()
,t.interrupt()
时- 竞争锁成功,t 线程从
WAITING --> RUNNABLE
- 竞争锁失败,t 线程从
WAITING --> BLOCKED
- 竞争锁成功,t 线程从
@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj) {
log.debug("执行....");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码...."); // 断点3
}
},"t1").start();
new Thread(() -> {
synchronized (obj) {
log.debug("执行....");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码...."); // 断点2
}
},"t2").start();
// 主线程两秒后执行
sleep(0.5);
log.debug("唤醒 obj 上其它线程");
synchronized (obj) {
obj.notifyAll(); // 唤醒obj上所有等待线程 断点1
}
}
}
/*Debug观察主线程、t1、t2状态:
*/
Debug
观察主线程、t1、t2状态:
- 断点1执行前:主线程-
RUNNING
, t1-WAITING
, t2-WAITING
- 断点1执行后:主线程-
RUNNING
, t1-MONITOR
, t2-MONITOR
(因为这时主线程还没执行完,还有一行) - 执行到断点2,代表t2竞争到锁:t1-
MONITOR
, t2-RUNNING
- 执行到断点1,代表t2结束释放锁,t1竞争到锁:t1-
RUNNING
根据调试结果我们发现其实还有一种情况为 WAITING <--> BLOCKED
,即唤醒线程后竞争锁失败后线程会进入WaitSet
等待,对应上面的MONITOR
状态,上面并没有画出。
情况 3 RUNNABLE <--> WAITING
- 当前线程调用
t.join()
方法时,当前线程从RUNNABLE --> WAITING
- 注意是当前线程在t线程对象的监视器上等待,这个涉及到join的原理,请看我另一篇讲
join
的博客
- 注意是当前线程在t线程对象的监视器上等待,这个涉及到join的原理,请看我另一篇讲
- t 线程运行结束,或调用了当前线程的
interrupt()
时,当前线程从WAITING --> RUNNABLE
情况 4 RUNNABLE <--> WAITING
- 当前线程调用
LockSupport.park()
方法会让当前线程从RUNNABLE --> WAITING
- 调用
LockSupport.unpark
(目标线程) 或调用了目标线程 的interrupt()
,会让目标线程从WAITING --> RUNNABLE
情况 5 RUNNABLE <--> TIMED_WAITING
t 线程用 synchronized(obj)
获取了对象锁后
- 调用
obj.wait(long n)
方法时,t 线程从RUNNABLE --> TIMED_WAITING
- t 线程等待时间超过了 n 毫秒,或调用
obj.notify()
,obj.notifyAll()
,t.interrupt()
时- 竞争锁成功,t 线程从
TIMED_WAITING --> RUNNABLE
- 竞争锁失败,t 线程从
TIMED_WAITING --> BLOCKED
- 竞争锁成功,t 线程从
情况 6 RUNNABLE <--> TIMED_WAITING
- 当前线程调用
t.join(long n)
方法时,当前线程从RUNNABLE --> TIMED_WAITING
- 注意是当前线程在t 线程对象的监视器上等待
- 当前线程等待时间超过了 n 毫秒,或t 线程运行结束,或调用了当前线程的
interrupt()
时,当前线程从TIMED_WAITING --> RUNNABLE
情况 7 RUNNABLE <--> TIMED_WAITING
- 当前线程调用
Thread.sleep(long n)
,当前线程从RUNNABLE --> TIMED_WAITING
- 当前线程等待时间超过了 n 毫秒,当前线程从
TIMED_WAITING --> RUNNABLE
情况 8 RUNNABLE <--> TIMED_WAITING
- 当前线程调用
LockSupport.parkNanos(long nanos)
或LockSupport.parkUntil(long millis)
时,当前线程从RUNNABLE --> TIMED_WAITING
- 调用
LockSupport.unpark(目标线程)
或调用了目标线程的interrupt()
,或是等待超时,会让目标线程从TIMED_WAITING--> RUNNABLE
情况 9 RUNNABLE <--> BLOCKED
- t 线程用
synchronized(obj)
获取了对象锁时如果竞争失败,从RUNNABLE --> BLOCKED
- 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有
BLOCKED
的线程重新竞争,如果其中 t 线程竞争 成功,从BLOCKED --> RUNNABLE
,其它失败的线程仍然BLOCKED
情况 10 RUNNABLE <--> TERMINATED
- 当前线程所有代码运行完毕,进入
TERMINATED