【J.U.C】线程池之工作流程与ThreadPoolExecutor介绍
线程池工作流程
- 如果当前运行的线程少于corePoolSize,则创建新线程(核心线程)来执行任务。
- 如果运行的线程等于或多于corePoolSize ,则将任务加入BlockingQueue。
- 如果BlockingQueue队列已满,则创建新的线程(非核心)来处理任务。
- 如果核心线程与非核心线程总数超出maxiumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。
ThreeadPoolExecutor
1 | public ThreadPoolExecutor(int corePoolSize, |
- corePoolSize
除非设置了{@code allowCoreThreadTimeOut},否则要保留在池中的线程数,即使它们是空闲的。 - maximumPoolSize
池中允许的最大线程数。 - keepAliveTime
当线程数大于内核时,这是多余的空闲线程在终止新任务之前等待新任务的最长时间。 - unit
{@code keepAliveTime}参数的时间单位。 - workQueue
用于在任务执行前保存任务的队列。这个队列只包含{@code execute}方法提交的{@code Runnable}任务。 - threadFactory
执行程序创建新线程时使用的工厂。 - handler
由于达到线程边界和队列容量而阻塞执行时使用的处理程序。
BlockingQueue
- SynchronousQueue
不存储元素的阻塞队列,一个插入操作,必须等待移除操作结束,每个任务一个线程。
使用的时候maximumPoolSize一般指定成Integer.MAX_VALUE。 - LinkedBlockingQueue
如果当前线程数大于等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中。 - ArrayBlockingQueue
可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误。 - DelayQueue
队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。 - priorityBlockingQuene
具有优先级的无界阻塞队列。
RejectedExecutionHandler
有4个ThreeadPoolExecutor内部类。
- AbortPolicy
直接抛出异常,默认策略。 - CallerRunsPolicy
用调用者所在的线程来执行任务。 - DiscardOldestPolicy
丢弃阻塞队列中靠最前的任务,并执行当前任务。
4、DiscardPolicy
直接丢弃任务。
最好自定义饱和策略,实现RejectedExecutionHandler接口,如:记录日志或持久化存储不能处理的任务。
线程池大小设置
- CPU密集型
尽量使用较小的线程池,减少CUP上下文切换,一般为CPU核心数+1。 - IO密集型
可以适当加大线程池数量,IO多,所以在等待IO的时候,充分利用CPU,一般为CPU核心数2倍。
但是对于一些特别耗时的IO操作,盲目的用线程池可能也不是很好,通过异步+单线程轮询,上层再配合上一个固定的线程池,效果可能更好,参考Reactor模型,后期总结。 - 混合型
视具体情况而定。
任务提交
- Callable
通过submit函数提交,返回Future对象。 - Runnable
通过execute提交,没有返回结果。
关闭线程池
- shutdown()
仅停止阻塞队列中等待的线程,那些正在执行的线程就会让他们执行结束。 - shutdownNow()
不仅会停止阻塞队列中的线程,而且会停止正在执行的线程。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ClawHub的技术分享!