一、构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数解析:
int corePoolSize:在线程池中保持线程的数量,即使是空闲的
int maximumPoolSize:线程池中允许的线程的最大数量
long keepAliveTime:当线程数大于corePoolSize时,多余空闲线程终止前的最大等待时间
TimeUnit unit:keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue:用于在任务执行前保持任务的队列
ThreadFactory threadFactory:创建新线程的线程工厂
RejectedExecutionHandler handler:线程限制和队列容量达到,执行程序被阻塞的拒绝策略
TimeUnit 取值:
TimeUnit.NANOSECONDS; //纳秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.SECONDS; //秒
TimeUnit.MINUTES; //分钟
TimeUnit.HOURS; //小时
TimeUnit.DAYS; //天
workQueue 常见的阻塞队列:
ArrayBlockingQueue(有界队列)
LinkedBlockingQueue(无界队列)
SynchronousQueue
有界队列和无界队列的区别:
- 有界队列
有助于防止在使用有限最大PosiLosis时资源耗尽,但可以更难调整和控制。
初始的poolSize < corePoolSize,提交runnable任务,会直接做为new一个Thread的参数,立马执行 。
当提交的任务数超过了corePoolSize,会将当前的runable提交到一个block queue中。
有界队列满了之后,如果poolSize < maximumPoolsize时,会尝试new 一个Thread的进行救急处理,立马执行对应的runnable任务。
如果上面也无法处理了,就会走到第四步执行拒绝策略,防止资源耗尽。
- 无界队列
说LinkedBlockingQueue是无界队列是无预定义容量的时候,如果用无参构造函数初始化,默认的容量是Integer.MAX_VALUE
与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新的任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加,若后续仍有新的任务加入,而没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。
二、线程池种类
1、newSingleThreadExecutor
ExecutorService threadPool = Executors.newSingleThreadExecutor();
一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2、newFixedThreadPool
ExecutorService threadPool = Executors.newFixedThreadPool(3);
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3、newCachedThreadPool
ExecutorService threadPool = Executors.newCachedThreadPool();
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,此线程池又添加新线程来处理任务。
4、newScheduledThreadPool
ExecutorService pool = Executors.newScheduledThreadPool(10);
线程池支持定时以及周期性执行任务的需求。
5、newWorkStealingPool
ExecutorService threadPool = Executors.newWorkStealingPool();
newWorkStealingPool是jdk1.8才有的,会根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层用的ForkJoinPool来实现的。
ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。
三、拒绝策略
当请求不断进来,而系统此时又处理不过来的时候,需要采取的策略是拒绝服务。RejectedExecutionHandler接口提供了拒绝任务处理的自定义方法的机会。在ThreadPoolExecutor中已经包含四种处理策略。
1、AbortPolicy
该策略会直接抛出异常,阻止系统正常工作。抛出RejectedExecutionException
2、CallerRunsPolicy
只要线程池未关闭,该策略直接在调用者线程中,运行当前的被丢弃的任务。
3、DiscardPolicy
该策略默默的丢弃无法处理的任务,不予任何处理。
4、DiscardOldestPolicy
该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。
四、线程池关闭
1、shutdownNow
对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表
2、shutdown
线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务