前言
线程池ThreadPoolExecutor
在运行的过程中,业务并发量变动,需要不停服务调整线程池的线程数,ThreadPoolExecutor
支持动态调整corePoolSize
与maximumPoolSize
的值。
示例demo
1 | public class ThreadChangeTest { |
在程序运行中动态修改线程池corePoolSize
与maximumPoolSize
的值
源码分析
线程池参数调大
1 | public void setCorePoolSize(int corePoolSize) { |
源码看出:线程池的调节时直接设置corePoolSize
与maximumPoolSize
的值
其中
1 | workerCountOf(ctl.get()) |
代表工作任务线程数,参考我的博客JDK8线程池-ThreadPoolExecutor源码解析
调大corePoolSize
与maximumPoolSize
,线程池运行过程中自动生效,线程池处理逻辑增强。
线程池调小
调小corePoolSize
与maximumPoolSize
均会执行
1 | interruptIdleWorkers(); |
跟踪interruptIdleWorkers源码
1 | private void interruptIdleWorkers() { |
这里的workers
注意:是一个HashSet
,存放规则:
核心线程优先占满,即使核心线程有空闲,新任务来了会优先开启新的线程而不是复用,核心线程仅在占满才会复用,然后使用队列,最后使用max
线程,max
线程数对应的workers
会动态变化,
参考我的博客JDK8
线程池-ThreadPoolExecutor
源码解析
线程池任务执行源码
我们看ThreadPoolExecutor
执行任务的源码,参考我的博客JDK8
线程池-ThreadPoolExecutor
源码解析
1 | final void runWorker(Worker w) { |
可以看出在任务拿出来后,立即加锁
包括任务执行的过程都是加锁的。
加锁分析
1 | private final class Worker extends AbstractQueuedSynchronizer implements Runnable { |
使用了AQS,自定义了加锁方式CAS模式
1 | public abstract class AbstractQueuedSynchronizer |
可以看出使用tryAcquire
和tryRelease
,均重写方法
1 | protected boolean tryAcquire(int unused) { |
compareAndSetState(0, 1)
使用上面的代码加锁,意味着线程执行过程中都是加锁的,不会被销毁,只会销毁空闲线程,或者当前线程执行结束销毁。
线程池调小corePoolSize
与maximumPoolSize
对当前正在执行的任务没有影响。
调节队列大小
队列是不可以动态调整的。
1 | private final int capacity; |
总结
线程池
corePoolSize
与maximumPoolSize
调大注意max
线程数不要调过大,计算机资源是有限的。线程池的队列初始化大小注意,不能动态调节,队列占用的是堆内存,注意JVM的内存大小与GC能力,尽量减小大对象的存在。
线程池
corePoolSize
与maximumPoolSize
和队列调小注意,线程池的处理能力减弱,可能会执行拒绝策略。
参考地址
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员简栈文化-小助手(lastpass4u),他会拉你们进群。