JAVA ThreadPoolExecutor线程池
设计
ThreadPoolExecutor中存在内部类Worker, 线程池通过HashSet<Worker>保存这些Worker对象。Worker类实现Runnable接口,Worker对象就是实际的任务执行者,同时也代表一个独立线程。每一个Worker对象在实例化时通过ThreadFactory创建一个自己专属的线程。这个专属线程启动后执行这个Worker对象的run方法。Worker的run方法执行从BlockingQueue<Runnable>中取得任务,并执行任务runnable.run方法。
注意
1 不要用无限队列,应为可能造成大量任务堆积,内存无法释放
执行逻辑
任务提交到线程池后先交给核心线程,若无空闲则放入队列中,若队列已满则尝试交给备用线程。若以上都不能成功,则启动拒绝策略。
状态
由大到小依次是:
RUNNING:接受新的任务,处理队列任务;
SHUTDOWN:不在接受新的任务,处理队列任务;
STOP:不在接受新任务,不处理队列任务,中断正在执行的任务线程。shutDownNow();
TIDYING:所有的任务已经结束,任务线程为0,线程池转换到TIDYING;
TERMINATED:停止,线程池已结束,且无任务在执行,即terminated()方法执行完。
关闭线程池
可以通过调用线程池的shutdown()或shutdownNow()方法来关闭线程池。他们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无响应中断的任务可能永远无法停止。只要调用了这两个关闭方法的一个,isShutdown()就会返回true。当所有的任务都关闭后,才表示线程池关闭成功,这是调用isTerminated()方法会返回true。shutdown方法只是发出了停止信号,等所有任务执行完毕会关闭线程池;而shutdownNow则是立即停止所有任务,返回未执行任务List<Runable>(但是已执行未完成被终止的任务不包含),调用isTerminated()方法会返回true。
isTerminated当调用shutdown()方法后,并且所有提交的任务完成后返回为true;
isTerminated当调用shutdownNow()方法后,成功停止后返回为true;
线程数配置
分布式时要考虑多机生产消费能力和通讯消耗。
最低线程数 : 依据生产者的每秒平均生产能力、单一线程每秒的平均消费能力。
最高线程数 : 依据生产者的每秒可能的峰值生产能力 、单一线程每秒的可能的最低/平均消费能力。
缓冲线程数: 依据情况而定
1) CPU密集型任务的线程池配置
因为cpu执行速度非常快,往往切换线程上下文环境所耗费的时间比执行代码花费的时间更长,所以CPU密集型任务的线程数应该尽量保持和CPU核数一致,以减少线程切换带来的性能损耗。
2) IO密集型任务的线程池配置
IO密集型任务的主要性能瓶颈在于等待IO结果,而CPU利用率比较低,当遇到任务中存在从DB中读取数据、从缓存中读取数据时、远程通信等,就可以认为是IO密集型任务。一般而言业务系统配置线程池基本上都会采用上边的模型配置。