举例
烧烤店吃烧烤。
- 店内10个桌(corePoolSize),店外5个桌,这个店最多同时吃10个桌(maximumPoolSize)
- 店外城管不让占道,所以店内10个桌满了,先排队 (BlockingQueue)
- 这队长度,有的理论最大值 Integer.MAX_VALUE,几乎没限制
- 有的有限制 bounded
- 有的店员就拉一个人,还拉着不让你走,一有桌子,立马让你上桌
- 队都满了,那就店外开桌 (5桌)
- 店外都超5桌了,那就看店家啥策略了 (RejectedExecutionHandler)
- 有的直接砸店,不干了 (AbortPolicy,这不是傻瓜嘛)
- 有的让最早排队的滚蛋,老人等的不耐烦了 (DiscardOldestPolicy)
- 有的让最晚排队的滚蛋,新人不想等 (DiscardPolicy)
- 有的就让你现在站着吃,不用入桌子了 (CallerRunsPolicy 哈哈哈哈哈哈)
- 这店外的桌子,得尽快收拾,过段时间 (keepAliveTime)都没人入座,就把店外桌子收起来。
- 店内默认不收桌子,你要允许收 (allowCoreThreadTimeOut),那过段时间(keepAliveTime)没人做,也把桌子收起来
修复方法
1. 修改 reject 策略
提交的task超出 线程池最大限制(maximumPoolSize + queued tasks size 池子里的最大数+进池子最长排队数),就会触发 reject 策略。默认策略为 AbortPolicy (抛出异常),
- reject策略 继承自:RejectedExecutionHandler
- 换成其他子类:
- DiscardOldestPolicy (抛弃 task等待执行队列中 最早加入的task),除非Executor被shutdown,那就不执行了
- DiscardPolicy 抛弃当前的task
- CallerRunsPolicy 执行execute的线程运行,不异步了。除非Executor被shutdown,那就不执行了
- AbortPolicy 抛出异常
2. 扩大允许线程最大 数量
最大并发数就是:maximumPoolSize + queued tasks size = 25个
- 扩大maximumPoolSize数
- 扩大 queued 的capacity(容量)
- 比如采用 LinkedBlockingDeque, capacity 是 Integer.MAX_VALUE
- LinkedBlockingDeque 不直接创建队列,
- 如果用了ArrayBlockingQueue,会直接创建 最大数数组,会消耗资源,不推荐这个设置最大值
- SynchronousQueue 的容量是0(为什么是0)
- 比如采用 LinkedBlockingDeque, capacity 是 Integer.MAX_VALUE
文档
看下文档,thread pool 属性: corePoolSize核心线程数、maximumPoolSize 最大线程数、BlockingQueue阻塞队列、RejectedExecutionHandler抛弃线程策略、keepAliveTime
当提交新 task时
- 优先创建核心线程: 现有线程 < corePoolSiz, 创建新线程
- 核心线程不被回收,除非 allowCoreThreadTimeOut ,则超出 keepAliveTime 后被回收。
- 优先队列排队:corePoolSize < 现有线程数
- 队列满了,则创建新线程
- task执行完,thread变空闲,超出 keepAliveTime 时间则回收线程
- 线程数大于maximumPoolSize,启动RejectedExecutionHandler,默认实现类为 AbortPolicy,抛出异常
测试代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Description:
* <p>
* Create by zhaojialiang02 2022/9/23 2:09 下午
*/
public class TestThread {
final static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
4,
15,
200,
TimeUnit.MINUTES,
new ArrayBlockingQueue(10));
public static void main(String[] args) {
final int count = 40;
for (int j = 0; j<3; j++) {
final int jParam = j;
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End first thread group: " + jParam);
}
});
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("First wake up");
for (int i = 0; i < count; i++) {
System.out.println("1Generate thread:" + i);
final int index = i;
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("++++++Start thread:" + index);
try {
Thread.sleep(1000 * 60 * 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------End thread:" + index);
}
});
}
try {
Thread.sleep(1000 * 60 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------App GG");
}
}
创建线程池的API
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
报错信息
///.....
++++++Start thread:23
1Generate thread:25
++++++Start thread:24
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task TestThread$2@74a14482 rejected from java.util.concurrent.ThreadPoolExecutor@1540e19d[Running, pool size = 15, active threads = 15, queued tasks = 10, completed tasks = 3]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at TestThread.main(TestThread.java:49)
Running, pool size = 15, active threads = 15, queued tasks = 10, completed tasks = 3