executor框架的由来:
为每个线程分配一个线程是不太现实的:
1.线程的生命周期开销太高
2.资源耗尽
3.稳定性,不同平台客使用的线程数不同,而且受制于JVM启动参数,Tread构造中的请求栈大小,以及底层操作系统对线程的限制。
executor实现了对生命周期的支持,信息收集、应用程序管理机制和性能监视等机制。
一、Java中创建线程池很简单,只需要调用Executors
中相应的便捷方法即可,,最常用的三种线程池:
1.newFixedThreadPool(int nThreads) 创建固定大小的线程池,调用ThreadPoolExecutor的5个参数的构造器:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
2.newSingleThreadExecutor() 创建只有一个线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
3.newCachedThreadPool() 创建一个不限线程数上限(实际最大为Integer.MAX,近视无上限)的线程池,任何提交的任务都将立即执行
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
二、. ThreadPoolExecutor的7大构造参数:
public ThreadPoolExecutor( int corePoolSize, // 线程池长期维持的线程数,即使线程处于Idle状态,也不会回收。
int maximumPoolSize, // 线程数的上限
long keepAliveTime, TimeUnit unit, // 超过corePoolSize的线程的idle时长, // 超过这个时间,多余的线程会被回收。
BlockingQueue workQueue, // 任务的排队队列
ThreadFactory threadFactory, // 新线程的产生方式
RejectedExecutionHandler handler) // 拒绝回掉
这些参数中,比较容易引起问题的有corePoolSize
, maximumPoolSize
, workQueue
以及handler
:
corePoolSize
和maximumPoolSize
设置不当会影响效率,甚至耗尽线程;workQueue
设置不当容易导致OOM;handler
设置不当会导致提交任务时抛出异常。拒绝策略 | 拒绝行为 |
---|---|
AbortPolicy | 抛出RejectedExecutionException |
DiscardPolicy | 什么也不做,直接忽略 |
DiscardOldestPolicy | 丢弃执行队列中最老的任务,尝试为当前提交的任务腾出位置 |
CallerRunsPolicy | 直接由提交任务者执行这个任务 |
线程池默认的拒绝行为是AbortPolicy
,也就是抛出RejectedExecutionHandler
异常,该异常是非受检异常,很容易忘记捕获。如果不关心任务被拒绝的事件,可以将拒绝策略设置成DiscardPolicy
,这样多余的任务会悄悄的被忽略。
三、ExecutorService的生命周期
JVM必须等待所有线程结束后,才会正常退出,无法关闭executor,jvm将无法正确退出。
1.运行
2.关闭
3.已终止
shutdown将继续执性已经提交的任务,包括还未执行的任务。shutdownNow是一种粗暴的关闭线程池的手段,它将尝试关闭,不会再启动队列 里的任务。
ExecutorService必须显示调用关闭(shutdown或shutdownNow),不然ExecutorService一直处于运行态。
ExecutorService关闭后,再提交新的任务,将抛出RejecException的未检查异常,等待所有任务执行完毕后,ExcecutorService进入已终止状态。(可以显示调用awaitTermination等待终止,通常在此之后会立即调用shutdown,也可以使用isTerminated轮询,查询ExecutorService状态)
注意:一般不会直接使用executor,存在oom问题,在一些大公司会对原始的executor进行封装使用。
四、线程池配置管理
1.考虑CPU密集型和IO密集型
2.考虑硬件核数。
参考:https://www.cnblogs.com/CarpenterLee/p/9558026.html