Java 线程

线程状态

先来个开场四连问

  • Java线程状态有几个?
  • Java线程状态是如何转换?
  • Java线程状态转换什么情况会进入锁的等待队列?
  • Java线程状态转换什么情况会进入锁的同步队列?

2021-08-02-16-37-35-325327.png
首先线程的状态可以分为6态或7态,具体状态如下
6态

  • New:新建状态
  • Runnable:可运行状态
  • Terminated:终止状态
  • Waiting:等待状态
  • TimedWaiting:超时等待状态
  • Blocked:阻塞状态

7态

  • New:新建状态
  • Ready:就绪状态
  • Running:运行状态
  • Terminated:终止状态
  • Waiting:等待状态
  • TimedWaiting:超时等待状态
  • Blocked:阻塞状态

其实6态与7态差别不大,只不过7态把Runnable可运行状态,拆解成了Ready就绪状态与Running运行状态。
以7态为例,来逐步讲解它们之间是如何转换的。

新建状态(New)

可以通过实现Runnable接口或继承Thread声明一个线程类,new一个实例后,线程就进入了新建状态。
2021-08-02-16-37-35-422855.png
一个刚诞生的线程,处于新建状态。

就绪状态(Ready)

线程对象创建成功后,调用该线程的start()函数,线程进入就绪状态,该状态的线程进入可运行线程池中,等待获取C P U的使用权。
2021-08-02-16-37-35-558925.png
线程表示,已经准备好了,此时是就绪状态,快选我吧~

运行状态(Running)

此时线程调度程序正在从可运行线程池中选择一个线程,该线程进入运行状态。
换句话说,线程获取到了C P U时间片。
2021-08-02-16-37-35-691923.png
还没完呢,当线程时间片用完或调用的yield()函数,该线程回到就绪状态。
2021-08-02-16-37-35-838911.png
作为一名运气好的线程,进入了运行状态,但是运气用完了,还得回到就绪状态。

终止状态(Terminated)

线程继续运行,直到执行结束或执行过程中因异常意外终止都会使线程进入终止状态。
线程一旦终止,就不能复生,这是不可逆的过程。
2021-08-02-16-37-36-003509.png
线程的人生迎来了终点,可能一帆风顺过完一生,也可能英年早逝令人惋惜。

等待状态(Waiting)

运行状态的线程执行wait()join()LockSupport.park()任意函数,该线程进入等待状态。
其中wait()join()函数会让J V M把该线程放入锁等待队列。
处于这种状态的线程不会被分配C P U执行时间,它们要等待被主动唤醒,否则会一直处于等待状态。
2021-08-02-16-37-36-147470.png
如果要唤醒线程怎么办呢?
执行LockSupport.unpark(t)函数唤醒指定线程,该线程回到就绪状态。
而通过notify()notifyAll()、join线程执行完毕方式,会唤醒锁等待队列的线程,出队的线程回到就绪状态。
2021-08-02-16-37-36-402000.png

超时等待状态(Timed waiting)

超时等待与等待状态一样,唯一的区别就是多了超时机制,不会一直等待被其他线程主动唤醒,而是到达指定时间后会自动唤醒。
以下函数会触发进入超时等待状态

  • wait(long)
  • join(long)
  • LockSupport.parkNanos(long)
  • LockSupport.parkUtil(long)
  • sleep(long)

其中wait(long)join(long)函数会让J V M把线程放入锁等待队列。
2021-08-02-16-37-36-819438.png
后面的唤醒剧情就和等待状态如出一辙,就多了超时时间到了,自动唤醒的动作。
2021-08-02-16-37-37-317949.png

阻塞状态(Blocked)

运行状态的线程获取同步锁失败或发出I/O请求,该线程进入阻塞状态。
如果是获取同步锁失败J V M还会把该线程放入锁的同步队列。
2021-08-02-16-37-37-538081.png
同步锁被释放时,锁的同步队列会出队所有线程,进入就绪状态。
I/O处理完毕时,该线程重新回到就绪状态。
2021-08-02-16-37-37-795180.png

小结

最后放一张简化的线程转换图。
2021-08-02-16-37-37-951441.png