线程池-动态更新线程池参数
import java.util.concurrent.*;
public class ThreadChangeDemo {
public static void main(String[] args) throws InterruptedException {
dynamicModifyExecutor();
}
/**
* 自定义线程池
* */
private static ThreadPoolExecutor buildThreadPoolExecutor(){
return new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadPoolExecutor.CallerRunsPolicy());
}
/**
* 提交任务给线程池,修改线程池参数
* */
private static void dynamicModifyExecutor() throws InterruptedException {
ThreadPoolExecutor executor = buildThreadPoolExecutor();
for (int i=0;i<15;i++){
executor.submit(()->{
threadPoolStatus(executor,"创建任务");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println(Thread.currentThread().getName()+"当前线程完成任务");
});
}
threadPoolStatus(executor,"改变之前");
TimeUnit.SECONDS.sleep(1);
executor.setCorePoolSize(10);
executor.setMaximumPoolSize(10);
executor.prestartAllCoreThreads();
threadPoolStatus(executor,"改变之后");
Thread.currentThread().join();
}
/**
* 打印线程池状态
* */
private static void threadPoolStatus(ThreadPoolExecutor executor,String name){
LinkedBlockingQueue queue= (LinkedBlockingQueue) executor.getQueue();
System.out.println(Thread.currentThread().getName()+"-"+name+"-:"+
" 核心线程数:"+executor.getCorePoolSize()+
" 活动线程数:"+executor.getActiveCount()+
" 最大线程数:"+executor.getMaximumPoolSize()+
" 线程池活跃度:"+divide(executor.getActiveCount(),executor.getMaximumPoolSize())+
" 任务完成数:"+executor.getCompletedTaskCount()+
" 队列大小:"+(queue.size()+queue.remainingCapacity())+
" 当前排队线程数:"+queue.size()+
" 队列剩余大小:"+queue.remainingCapacity()+
" 队列使用度:"+divide(queue.size(),queue.size()+queue.remainingCapacity()));
}
/**
*保留两位小数
* */
private static String divide(int num1,int num2){
return String.format("%1.2f%%",Double.parseDouble(num1+"")/Double.parseDouble(num2+"")*100);
}
}
上面的程序就是自定义了一个核心线程数为 2,最大线程数为 5,队列长度为 10 的线程池。
然后给它塞 15 个耗时 10 秒的任务,直接让它 5 个最大线程都在工作,队列长度 10 个都塞满。
当前的情况下,队列里面的 10 个,前 5 个在 10 秒后会被执行,后 5 个在 20 秒后会被执行。
再加上最大线程数正在执行的 5 个,15 个任务全部执行完全需要 3 个 10 秒即 30 秒的时间。
这个时候,如果我们把核心线程数和最大线程数都修改为 10。
那么 10 个任务会直接被 10 个最大线程数接管,10 秒就会被处理完成。
剩下的 5 个任务在队列里面,在 10 秒后被执行完成。
所以,15 个任务执行完成需要 2 个 10 秒即 20 秒的时间处理完成了。
看一下上面程序的打印日志:
pool-1-thread-3-创建任务-: 核心线程数:2 活动线程数:5 最大线程数:5 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:10 队列剩余大小:0 队列使用度:100.00%
pool-1-thread-2-创建任务-: 核心线程数:2 活动线程数:5 最大线程数:5 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:10 队列剩余大小:0 队列使用度:100.00%
pool-1-thread-1-创建任务-: 核心线程数:2 活动线程数:5 最大线程数:5 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:10 队列剩余大小:0 队列使用度:100.00%
pool-1-thread-5-创建任务-: 核心线程数:2 活动线程数:5 最大线程数:5 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:10 队列剩余大小:0 队列使用度:100.00%
main-改变之前-: 核心线程数:2 活动线程数:5 最大线程数:5 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:10 队列剩余大小:0 队列使用度:100.00%
pool-1-thread-4-创建任务-: 核心线程数:2 活动线程数:5 最大线程数:5 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:10 队列剩余大小:0 队列使用度:100.00%
pool-1-thread-12-创建任务-: 核心线程数:10 活动线程数:9 最大线程数:10 线程池活跃度:90.00% 任务完成数:0 队列大小:10 当前排队线程数:6 队列剩余大小:4 队列使用度:60.00%
pool-1-thread-13-创建任务-: 核心线程数:10 活动线程数:9 最大线程数:10 线程池活跃度:90.00% 任务完成数:0 队列大小:10 当前排队线程数:6 队列剩余大小:4 队列使用度:60.00%
pool-1-thread-15-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:5 队列剩余大小:5 队列使用度:50.00%
main-改变之后-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:5 队列剩余大小:5 队列使用度:50.00%
pool-1-thread-14-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:5 队列剩余大小:5 队列使用度:50.00%
pool-1-thread-16-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:5 队列剩余大小:5 队列使用度:50.00%
pool-1-thread-1-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:4 队列大小:10 当前排队线程数:1 队列剩余大小:9 队列使用度:10.00%
pool-1-thread-5-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:4 队列大小:10 当前排队线程数:1 队列剩余大小:9 队列使用度:10.00%
pool-1-thread-2-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:4 队列大小:10 当前排队线程数:1 队列剩余大小:9 队列使用度:10.00%
pool-1-thread-3-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:4 队列大小:10 当前排队线程数:1 队列剩余大小:9 队列使用度:10.00%
pool-1-thread-4-创建任务-: 核心线程数:10 活动线程数:10 最大线程数:10 线程池活跃度:100.00% 任务完成数:5 队列大小:10 当前排队线程数:0 队列剩余大小:10 队列使用度:0.00%
Process finished with exit code 1
setCorePoolSize()源码
/**
* Sets the core number of threads. This overrides any value set
* in the constructor. If the new value is smaller than the
* current value, excess existing threads will be terminated when
* they next become idle. If larger, new threads will, if needed,
* be started to execute any queued tasks.
*
* @param corePoolSize the new core size
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @see #getCorePoolSize
*/
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers();
else if (delta > 0) {
// We don't really know how many new threads are "needed".
// As a heuristic, prestart enough new workers (up to new
// core size) to handle the current number of tasks in
// queue, but stop if queue becomes empty while doing so.
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true)) {
if (workQueue.isEmpty())
break;
}
}
}
在运行期线程池使用方调用此方法设置corePoolSize之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。
对于最新的核心线程数小于当前工作线程数的情况,说明有多余的worker线程,此时会向当前idle的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收;
当最新的核心线程数大于之前核心线程数时,不是一致性增加差额的线程数,而是一个一个线程创建,创建完一个再去查询任务队列是否有任务;有任务则再次创建新的worker线程来执行队列任务,没有任务则就此停止,setCorePoolSize具体流程如下: