1、复制一份CapacityLinkedBlockingQueue ,修改名字为ReWritedCapacityLinkedBlockingQueue ,目的是为了调整线程池队列大小。因为 CapacityLinkedBlockingQueue 中的capacity是被final定义了的,所以要想在运行时调整队列大小,需要重新定义。在 ReWritedCapacityLinkedBlockingQueue 中,将 capacity 修改为:
private volatile int capacity;
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
2、阿里建议直接使用线程池底层方法来创建自己的线程池,避免OOM或系统资源浪费的情况出现。
/**
* 自定义线程池
*/
public static ThreadPoolExecutor buildThreadPoolExecutor(){
return new ThreadPoolExecutor(4,
4,
4,
TimeUnit.SECONDS,
new ReWritedCapacityLinkedBlockingQueue<>(20),
new NamedThreadFactory("我的线程"));
}
3、线程池动态调整方法:
threadPoolStatus(executor,"改变之前");
//最小核心线程数和最大线程数的值,设置为相同最佳
executor.setCorePoolSize(10);
executor.setMaximumPoolSize(10);
executor.prestartAllCoreThreads();
ReWritedCapacityLinkedBlockingQueue reWritedCapacityLinkedBlockingQueue =
(ReWritedCapacityLinkedBlockingQueue) executor.getQueue();
reWritedCapacityLinkedBlockingQueue.setCapacity(50);
threadPoolStatus(executor,"改变之后");
Thread.currentThread().join();
4、根据线程池的使用状态,对核心线程数、线程池最大值、队列进行自动调整或阿波罗等配置文件调整的方式管理。我因为更喜欢自动化,同时觉得修改配置文件还得有人随时监视系统情况,所以更倾向于没有人干预的方式,程序根据系统资源消耗动态调整。随即根据业务需要写了个简单的逻辑来实现。业务场景是基于获取网络摄像头视频流做人脸检测和人脸识别,获取人脸识别的方式是抓帧处理视频图像,这样自然就对机器的资源有所需求,在通过欧氏距离做了算法上的优化之后,避免了每一帧都进行识别比对,减少了系统的资源消耗。但是仍旧不可避免地存在多张人脸同时出现在视频画面中的时候需要消耗更多的系统资源对每张人脸进行图像处理和人脸库比对的情况。这样单线程必然处理不过来,同时还会造成视频检测的实时性随着计算过程的时间消耗而越来越大,所以采用多线程就是解决方案之一。在这种情况下,时而一个人脸,时而N个人脸,线程池就必然存在着动态调整的需要。所以参考了美团技术团队分享的经验,实现了想要的这个方案。
if (executor.getActiveCount() / executor.getMaximumPoolSize() >= 0.7) {
//最小核心线程数和最大线程数的值,设置为相同最佳
executor.setCorePoolSize(Double.valueOf(Math.round(executor.getCorePoolSize() * 1.5)).intValue());
executor.setMaximumPoolSize(Double.valueOf(Math.round(executor.getMaximumPoolSize() * 1.5)).intValue());
executor.prestartAllCoreThreads();
reWritedCapacityLinkedBlockingQueue.setCapacity(Double.valueOf(Math.round(reWritedCapacityLinkedBlockingQueue.getCapacity() * 1.2)).intValue());
TheCustomDynamicThreadPoolUtil.threadPoolStatus(executor, "线程池扩容之后");
}
if (executor.getActiveCount() / executor.getMaximumPoolSize() < 0.6) {
//核心线程数是否大于2,如果大于2,判断活跃线程数扩大1.5倍后是否不为0(有可能在程序执行到此刻的时候,唯一一个执行任务的线程已经完成了任务,活跃线程就会为0)
//如果核心线程数大于2为真,则核心线程数/2,或通过活跃线程数*1.5进行扩容(条件为,活跃线程数扩大1.5倍后是否为0
//如果核心线程数大于2为假,那么直接设定线程数的最低值2 math.round 四舍五入取整
executor.setCorePoolSize(executor.getCorePoolSize() > 2 ?
(Double.valueOf(Math.round(executor.getActiveCount() * 1.5)).intValue() == 0 ? Double.valueOf(Math.round(executor.getCorePoolSize() / 2)).intValue() :
Double.valueOf(Math.round(executor.getActiveCount() * 1.5)).intValue()) : 2);executor.setMaximumPoolSize(executor.getMaximumPoolSize() > 2 ?
(Double.valueOf(Math.round(executor.getActiveCount() * 1.5)).intValue() == 0 ?
Double.valueOf(Math.round(executor.getMaximumPoolSize() / 2)).intValue() :
Double.valueOf(Math.round(executor.getActiveCount() * 1.5)).intValue()) : 2);
executor.prestartAllCoreThreads();
//如果队列大于5,则进行-5后的结果判断,如果减5后大于等于5,那么执行减5操作,如果大于等于5不成立,则直接修改队列数量为5
reWritedCapacityLinkedBlockingQueue.setCapacity(reWritedCapacityLinkedBlockingQueue.getCapacity() > 5 ?
(Math.max(reWritedCapacityLinkedBlockingQueue.getCapacity() - 5, 5)) : 5);
TheCustomDynamicThreadPoolUtil.threadPoolStatus(executor, "线程池收缩之后");
}