[Q&A] newCachedThreadPool
定义
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 设置为0,即corePool为空;
Integer.MAX_VALUE, 设置为Integer.MAX_VALUE,即是无界的。
60L, TimeUnit.SECONDS, 意味着池中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。
new SynchronousQueue<Runnable>());
}
[Q&A] newCachedThreadPool可能存在的问题
CachedThreadPool使用没有容量的SynchronousQueue
作为线程池的工作队列,但CachedThreadPool的maximumPool
是无界的。这意味着,如果主线程提交任务速度
高于maximumPool中线程处理任务速度
时,CachedThreadPool会不断创建新线程。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU
和内存
资源。
[Q&A] CachedThreadPool
的execute()
方法的执行示意图如下图所示
# 1)首先执行SynchronousQueue.offer(Runnable task)
如果当前maximumPool中有空闲线程正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行完成;
否则执行下面的步骤2)。
# 2)当初始maximumPool为空,或者maximumPool中当前没有空闲线程时
没有线程执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。
# 3)在步骤2)中新创建的线程将任务执行完后,会执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。
这个poll操作会让空闲线程最多在SynchronousQueue中等待60秒钟。如果60秒钟内主线程提交了一个新任务(主线程执行步骤1)),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止。
由于空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。
[Q&A] CachedThreadPool
的任务传递示意图
SynchronousQueue是一个没有容量的阻塞队列。每个插入操作必须等待另一个线程的对应移除操作,反之亦然。CachedThreadPool使用SynchronousQueue,把主线程提交的任务传递给空闲线程执行。
-----------------------------------------------------------------------------读书笔记摘自 书名:Java并发编程的艺术 作者:方腾飞;魏鹏;程晓明
常见用途
线程任务
public class Task implements Runnable {
private final String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " → " + name + " Start Time = " + new Date());
System.out.println(Thread.currentThread().getName() + " → " + name + " End Time = " + new Date());
}
}
缓存型线程池
public class cachedThreadPool {
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.out.println(Thread.currentThread().getName() + "线程: Start at: " + new Date());
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 1; i < 10; i++) {
System.out.println("添加了第" + i + "个任务类");
Thread.sleep(2);
exec.execute(new Task("线程名字" + i));
}
exec.shutdown();
System.out.println(Thread.currentThread().getName() + " 线程: Finished all threads at:" + new Date());
}
}
执行结果分析
执行结果 |
---|
main线程: Start at: Fri May 19 20:55:45 CST 2023 |
添加了第1个任务类 |
添加了第2个任务类 |
添加了第3个任务类 |
添加了第4个任务类 |
添加了第5个任务类 |
pool-1-thread-1 → 线程名字1 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-4 → 线程名字4 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-2 → 线程名字2 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字3 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-1 → 线程名字1 End Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-4 → 线程名字4 End Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字3 End Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-2 → 线程名字2 End Time = Fri May 19 20:55:45 CST 2023 |
添加了第6个任务类 |
pool-1-thread-3 → 线程名字5 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字5 End Time = Fri May 19 20:55:45 CST 2023 |
添加了第7个任务类 |
pool-1-thread-3 → 线程名字6 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字6 End Time = Fri May 19 20:55:45 CST 2023 |
添加了第8个任务类 |
pool-1-thread-3 → 线程名字7 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字7 End Time = Fri May 19 20:55:45 CST 2023 |
添加了第9个任务类 |
pool-1-thread-3 → 线程名字8 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字8 End Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字9 Start Time = Fri May 19 20:55:45 CST 2023 |
pool-1-thread-3 → 线程名字9 End Time = Fri May 19 20:55:45 CST 2023 |
main 线程: Finished all threads at:Fri May 19 20:55:45 CST 2023 |
从控制台结果可以看出: |
发现reuse的线程,线程池创建4 个线程来 执行这9 个任务 |
主线程的执行与线程池里的线程分开,有可能主线程结束了,但是线程池还在运行 |
放入线程池的线程并不一定会按其放入的先后而顺序执行 |