《Java线程池》:Executor生命周期
我们知道线程是有多种执行状态的,同样管理线程的线程池也有多种状态。JVM会在所有线程(非后台daemon线程)全部终止后才退出,为了节省资源和有效释放资源关闭一个线程池就显得很重要。有时候无法正确的关闭线程池,将会阻止JVM的结束。
线程池Executor是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态。这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行。因此关闭线程池可能出现一下几种情况:
1、平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务
2、立即关闭:取消所有正在执行和未执行的任务
另外关闭线程池后对于任务的状态应该有相应的反馈信息。
图1 描述了线程池的4种状态。
1、线程池在构造前(new操作)是初始状态,一旦构造完成线程池就进入了执行状态RUNNING。严格意义上讲线程池构造完成后并没有线程被立即启动,只有进行“预启动”或者接收到任务的时候才会启动线程。这个会后面线程池的原理会详细分析。但是线程池是出于运行状态,随时准备接受任务来执行。
2、线程池运行中可以通过shutdown()和shutdownNow()来改变运行状态。shutdown()是一个平缓的关闭过程,线程池停止接受新的任务,同时等待已经提交的任务执行完毕,包括那些进入队列还没有开始的任务,这时候线程池处于SHUTDOWN状态;shutdownNow()是一个立即关闭过程,线程池停止接受新的任务,同时线程池取消所有执行的任务和已经进入队列但是还没有执行的任务,这时候线程池处于STOP状态。
3、一旦shutdown()或者shutdownNow()执行完毕,线程池就进入TERMINATED状态,此时线程池就结束了。
isTerminating()描述的是SHUTDOWN和STOP两种状态。
isShutdown()描述的是非RUNNING状态,也就是SHUTDOWN/STOP/TERMINATED三种状态。
线程池的API如下:
其中shutdownNow()会返回那些已经进入了队列但是还没有执行的任务列表。awaitTermination描述的是等待线程池关闭的时间,如果等待时间线程池还没有关闭将会抛出一个超时异常。
对于关闭线程池期间发生的任务提交情况就会触发一个拒绝执行的操作。这是java.util.concurrent.RejectedExecutionHandler描述的任务操作。下一个小结中将描述这些任务被拒绝后的操作。
总结下这个小节:
1、线程池有运行、关闭、停止、结束四种状态,结束后就会释放所有资源
2、平缓关闭线程池使用shutdown()
3、立即关闭线程池使用shutdownNow(),同时得到未执行的任务列表
4、检测线程池是否正处于关闭中,使用isShutdown()
5、检测线程池是否已经关闭使用isTerminated()
6、定时或者永久等待线程池关闭结束使用awaitTermination()操作
上面关于线程池生命周期的内容来自于博客:http://www.blogjava.net/xylz/archive/2011/01/04/342316.html。
上面确实讲解的比较清晰了,但是,自己在看ThreadPoolExecutor类的源码过程中,发现,线程池的生命周期不只有:RUNNING、SHUTDOWN、STOP/TERMINATED四种状态,还有一个TIDYING状态。
在ThreadPoolExecutor类源码中截取内容如下:
* The runState provides the main lifecycle control, taking on values:
*
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
* When both queue and pool are empty
* STOP -> TIDYING
* When pool is empty
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
*
* Threads waiting in awaitTermination() will return when the
* state reaches TERMINATED.
*
* Detecting the transition from SHUTDOWN to TIDYING is less
* straightforward than you'd like because the queue may become
* empty after non-empty and vice versa during SHUTDOWN state, but
* we can only terminate if, after seeing that it is empty, we see
* that workerCount is 0 (which sometimes entails a recheck -- see
* below).
上面的英文比较简单,也比较好理解,这里就不翻译的,根据上面的意思,也就是说,线程池在SHUTDOWM/STOP到TERMINATED状态之间还存在一个TIDYING状态。这几个状态的变化是这样的,这里载贴一下,有兴趣的也可以去源码中看一看。
* RUNNING -> SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
* When both queue and pool are empty
* STOP -> TIDYING
* When pool is empty
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
小结
本节我们需要了解的就是线程池的生命周期,回顾下,线程池的状态有:
1、RUNNING
2、SHUNDOWN
3、STOP
4、TIDYING
5、TERMINATED
这几个状态的转化关系为:
1、调用shundown()方法线程池的状态由RUNNING——>SHUTDOWN
2、调用shutdowNow()方法线程池的状态由RUNNING——>STOP
3、当任务队列和线程池均为空的时候 线程池的状态由STOP/SHUTDOWN——–>TIDYING
4、当terminated()方法被调用完成之后,线程池的状态由TIDYING———->TERMINATED状态
参考资料
博客:http://www.blogjava.net/xylz/archive/2011/01/04/342316.html。