线程流转总览图如下:
1、在操作系统层面来看,线程有5种状态:
(1)新建
(2)就绪(有资格分到cpu,但还没拿到)
(3)运行(拿到cpu执行时间,并正在执行)
(4)阻塞(没资格分到cpu)
(5)终结
2、Java中线程的状态分为6种:
(1)新建(NEW):新创建了一个线程对象,但还没有调用start()方法。也就是这个线程对象里面的run()方法还没有被执行的时候。
(2)运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态都称为“运行”状态。线程对象创建后,调用了该对象的start()方法。此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。所以,如果一个正在运行的线程是Runnable状态,当它运行到任务的一半时,执行该线程的CPU被调度去做其他事情,导致该线程暂时不运行,它的状态依然不变,还是Runnable,因为它有可能随时被调度回来继续执行任务。
(3)阻塞(BLOCKED):表示线程阻塞于锁。
线程由Runnable状态进入到Blocked状态:只有一种途径,就是进入到synchronized代码块或方法中时,未获取到相应的monitor锁。
(4)等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
线程由Runnable状态进入waiting状态:
1>当线程中调用了没有设置Timeout参数的object.wait()方法
2>当线程调用了没有设置Timeout参数的Thread.join()方法
3>当线程调用了Locksupport.park()方法
关于Locksupportpark()方法,这里说一下,我们通过上面知道Blocked是针对synchronized monitor锁的,但是在Java中实际是有很多其他锁的,比如ReentrantLock等,在这些锁中,如果线程没有获取到锁则会直接进入Waiting状态,其实这种本质上它就是执行了Locksupportpark()方法进入了waiting状态
Blocked与waiting的区别
Blocked是在等待其他线程释放monitor锁
Waiting则是在等待某个条件,比如join的线程执行完毕,或者notify()/notifyAll()。
(5)超时等待(TIMED_WAITING):该状态不同于WAITING,它设置了超时时间,可以在指定的时间后自行返回。处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
线程由Runnable状态进入Timed waiting状态:
1>线程执行了设置了时间参数的Thread.sleep(long millis)方法
2>线程执行了设置了时间参数的object.wait(long timeout)方法
3>线程执行了设置了时间参数的Thread.join(long millis)方法
4>线程执行了设置了时间参数的Locksupport.parkNanos(long nanos)方法和
Locksupport.parkuntil(long deadline)方法。
(6)终止(TERMINATED):表示该线程已经执行完毕。
两种情况:
当线程的run()方法完成时,线程正常退出。
出现一个没有捕获的异常,终止了run()方法,最终导致意外终止。
这6种状态定义在Thread类的State枚举中,可查看源码。另外可以通过调用getState()方法来获取当前线程是什么状态。
3、线程状态流转:
(1)BLOCKED进入RUNNABLE:
上面说了,从RUNNABLE进入到BLOCKED状态只有一种形式就是进入到synchronized代码块中时,未获取到相应的monitor锁。那要从BLOCKED进入RUNNABLE状态,就是获取到monitor锁。
(2)WAITING进入RUNNABLE:
执行Locksupport.unpark(),或者Join线程运行结束,或者被中断。
这里说一个点,就是我们从RUNNABLE状态到WAITING状态不是可以通过调用wait()方法,那从WAITING进入RUNNABLE状态是不是调用notify()或notifyAll()也可以呢?其实通过其他线程调用notify()或notifyAll()来唤醒它,则它会直接进入Blocked状态,这里大家可能会有疑问,不是应该直接进入Runnable吗?这里需要注意一点,因为唤醒waiting线程的线程如果调用notify()或notifyAll(),要求必须首先持有该monitor锁,这也就是我们说的wait()、notify()必须在synchronized代码块中。所以处于waiting状态的线程被唤醒时拿不到该锁,就会进入Blocked状态,直到执行了notify()/notifyAll()唤醒它的线程执行完毕并释放monitor锁,才可能轮到它去抢夺这把锁,如果它能抢到,就会从Blocked状态回到Runnable状态。
(3)TIMED WAITING进入RUNNABLE:
对于Timed Waiting,它存在一个超时机制,就是说如果超时时间到了,那么系统就会自动直接拿到锁,或者执行Locksupport.unpark(),或者Join线程运行结束,或者被中断。
与WAITING进入RUNNABLE一样,在Timed Waiting中执行notify()和notifyAll()也是一样的道理,它们会先进入Blocked状态,当抢到锁资源后,再进入到Runnable状态。