关于如何合理设置线程池参数解决方案(ThreadPoolExecutor)
线程池参数有哪些
我们直接来看构造方法
...
public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6,
ThreadFactory var7, RejectedExecutionHandler var8) {
this.ctl = new AtomicInteger(ctlOf(-536870912, 0));
this.mainLock = new ReentrantLock();
this.workers = new HashSet();
this.termination = this.mainLock.newCondition();
if (var1 >= 0 && var2 > 0 && var2 >= var1 && var3 >= 0L) {
if (var6 != null && var7 != null && var8 != null) {
this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
//核心线程数
this.corePoolSize = var1;
//最大线程数
this.maximumPoolSize = var2;
//工作队列
this.workQueue = var6;
//线程存活时间
this.keepAliveTime = var5.toNanos(var3);
//线程工厂
this.threadFactory = var7;
//拒绝策略
this.handler = var8;
} else {
throw new NullPointerException();
}
} else {
throw new IllegalArgumentException();
}
}
...
根据构造方法我们可以观察有7个参数
最最重要的三个参数,该解决方案也是围绕着这三个参数进行展开。
corePoolSize
maximumPoolSize
workQueue
美团是这么做的
大家想一想,美团饭点时的业务峰值和凌晨的业务峰值一样吗?这就要求在高峰期的时候,处理业务的线程能覆盖住业务量(当然nginx负载均衡、或其它限流框架也能做到这点,这里仅讨论在一个服务内如何做到伸缩自如),所以根据业务峰去做出调整即为合理的解决方案。
线程池处理任务流程
根据上面的流程图,我们只需要在三个关键点去做到动态配置即可,下面一一列举
动态设置corePoolSize
ThreadPoolExecutor里面给我们提供了setCorePoolSize(int var1)方法,能让我们在线程池运行的过程中去动态设置核心线程数。
源码解析
public void setCorePoolSize(int var1) {
if (var1 < 0) {
throw new IllegalArgumentException();
} else {
int var2 = var1 - this.corePoolSize;
this.corePoolSize = var1;
if (workerCountOf(this.ctl.get()) > var1) {
this.interruptIdleWorkers();
} else if (var2 > 0) {
int var3 = Math.min(var2, this.workQueue.size());
while (var3-- > 0 && this.addWorker((Runnable) null, true) && !this.workQueue.isEmpty()) {
;
}
}
}
}
步骤:
1.首先判断传进来的var1是否符合要求、不符合直接抛出异常。
2.将corePoolSize 设置为var1。
3.workerCountOf(this.ctl.get())这个方法就是判断当前线程池正在工作的线程数,判断是否大于var1(ctl这个属性通过高位、低位来维护了线程数量,还维护了线程池的运行状态,为什么这么使用,因为在线程池的大多数方法中都需要去获取线程池工作线程的数量以及状态,AtomicInteger ctl本身是一个线程安全的对象),如果大于var1,则像正在工作的线程发出中断请求,以回收线程。
4.如果小于var1,会将var2(var1与旧corePoolSize 的差值)会跟现在阻塞队列的长度进行比较,获取比较小的那个值var3,取出阻塞队列var3个任务执行。
5.这样就完成了对一个正在运行的线程池动态改变corePoolSize 的操作。
动态设置maximumPoolSize
ThreadPoolExecutor里面给我们提供了setMaximumPoolSize(int var1)方法,能让我们在线程池运行的过程中去动态设置最大线程数。
源码解析
public void setMaximumPoolSize(int var1) {
if (var1 > 0 && var1 >= this.corePoolSize) {
this.maximumPoolSize = var1;
if (workerCountOf(this.ctl.get()) > var1) {
this.interruptIdleWorkers();
}
} else {
throw new IllegalArgumentException();
}
}
这个相对操作核心线程来说就比较简单了
步骤:
1.当然还是先判断传入的参数符不符合规范,不符合直接抛出异常
2.将maximumPoolSize 设置为var1
3.判断当前工作线程数是否大于var1,大于则向当前工作线程发出中断请求,回收线程
重写workQueue
因为阻塞队列中的capacity也就是初始化队列的长度使用final字符来进行修饰,所以当队列长度一旦确定就不可更改
美团技术团队的解决方法是将LinkedBlockingQueue重写了,也就是把capacity的final去掉,达到了通过改变capacity的值来达到控制阻塞队列长度的目的
创作不易,转载请标注!!!