线程的6种状态
- New(新建)
- Runnable(可运行)
- Blocked(阻塞)
- Waiting(等待)
- Timed Waiting(计时等待)
- Terminated(终止)
查看当前线程的状态:Thread.currentThread().getState()
New(新建)
New表示线程新创建但还未启动的状态,即:通过new Thread()创建,但还未调用start()方法。
一旦调用start(),就从New态转换为Runnable态
Runnable(可运行)
Java的Runnable状态对应操作系统线程状态的两种状态,分别是:Running和Ready,即Runnable状态的线程可能正在运行,也可能正在等待被分配CPU资源
因此当执行线程A的CPU被调度去执行其他任务,线程A依然是Runnable状态,因为CPU随时有可能被调度回来继续执行任务
阻塞状态
Java中阻塞状态通常不仅仅是 Blocked,实际上它包括三种状态,分别是 Blocked(被阻塞)、Waiting(等待)、Timed Waiting(计时等待),这三 种状态统称为阻塞状态
Blocked(阻塞)
什么时候进入Blocked状态
从Runnable进入Blocked状态,就是在线程A进入synchronized代码块或synchronized方法的时候没有抢到monitor锁,如果线程B释放锁,并且线程A抢到monitor锁,就会从Blocked状态回到Runnable状态
Waiting(等待)
什么时候进入Waiting状态
从Runnable进入Waiting状态,由三种可能:
- 没有设置 Timeout 参数的 Object.wait() 方法。
- 没有设置 Timeout 参数的 Thread.join() 方法。
- LockSupport.park() 方法。
第三种可能解释
Blocked状态仅仅针对synchronized monitor锁,但是在Java种还有很多其他锁,比如ReentrantLock,如果线程在获取这种锁时没有抢到该锁就会进入 Waiting 状态,因为本质上它执行了 LockSupport.park() 方法,所以会进入 Waiting 状态。
Blocked于Waiting的区别
Blocked 在等待其他线程释放 monitor 锁,而 Waiting 则是在等待某个条件,比如 join 的线程执行完毕,或者是 notify()/notifyAll() 。
Timed Waiting(计时等待)
Waiting(等待)和Timed Waiting(计时等待)两种状态非常相似,区别仅在于有没有时间限制,Timed Waiting 会等待超时,由系统自动唤醒,或者在超时前被唤醒信号唤醒。
- 设置了时间参数的 Thread.sleep(long millis) 方法;
- 设置了时间参数的 Object.wait(long timeout) 方法;
- 设置了时间参数的 Thread.join(long millis) 方法;
- 设置了时间参数的 LockSupport.parkNanos(long nanos) 方法和 LockSupport.parkUntil(long deadline) 方法。
如何从阻塞状态(Block、Waiting、Timed Waiting)转换成其他状态
Blocked(阻塞)转换成Runnable
Blocked的线程如果获得了monitor锁,就会 ==> Runnable(可运行)
Waiting(等待)如何转换成其他状态
Waiting比较特殊,因为它是不限时等待,所以它不会自己主动恢复到Runnable状态,因此需要被通知,或者其他线程中断/结束
比如:当执行了LockSupport.unpark(线程A),或者线程A.join()的线程运行结束/中断时才可以进入Runnable状态
Timed Waiting(计时等待)如何转换成其他状态
如果超时时间到了,且能够直接获取到锁(比如ReentrantLock)或者join的线程运行结束/被中断/调用了LockSupport.unpark(线程t),就会直接恢复成Runnable状态
如果其他线程调用notify()或notifyAll()来唤醒线程A会发生什么
首先唤醒线程A的线程B必须持有monitor锁,所以处于Waiting状态的线程被唤醒时拿不到monitor锁,所以会进入Blocked状态,只有线程B执行完毕并且释放monitor锁,才可能轮到线程A去抢夺monitor锁,如果抢到就会转换到Runnable状态,对于 Timed Waiting(计时等待)也是一样的
Terminated(终止)
如何进入 Terminated(终止)状态
- run() 方法执行完毕,线程正常退出。
- 出现一个没有捕获的异常,终止了 run() 方法,最终导致意外终止。
线程应该注意的点
如上图所示:
- 线程的状态是需要按照箭头方向来走的,比如线程从 New 状态是不可以直接进入 Blocked 状态的,它需要先经历 Runnable 状态。
- 线程生命周期不可逆:一旦进入 Runnable 状态就不能回到 New 状态;一旦被终止就不可能再有任何状态的变化。所以一个线程只能有一次 New 和 Terminated 状态,只有处于中间状态才可以相互转换。