前言
某日同一同事探讨线程池的几个关键参数:corePoolSize,maximumPoolSize,queueCapacity;进而围绕这几个参数引出几个点:
- a) 应用初始状态下,此时提交任务,将创建线程来处理任务;
- b) 当运行的线程数达到corePoolSize时,新提交的任务将如何操作?
- c) 当queueCapacity满时,新提交的任务将如何处理?
- d) 当运行的线程数达到maximumPoolSize时,新提交的任务该如何处理?
- e) corePoolSize+1个线程运行中,此时有一个工作任务处理完成,该完成任务的线程后续的状态是什么样的?
第一反应
以自己来设计线程池的思路出发,当时个人认为处理方式如下:
在场景b)时,应该会创建新线程来执行任务,直到线程数达到maximumPoolSize后,
再提交的任务将放在等待队列中,直到队列塞满(达到queueCapacity),
此时再提交的任务将根据配置的拒绝模式处理;
然而,被活生生的打脸,该同事好好喝了杯茶说:
JAVA中线程池的实现方式如下(默认maximumPoolSize > corePoolSize,queueCapacity有限大小):
1. 运行状态的线程数小于corePoolSize时,新增的任务将创建新线程来处理任务;
2. 当运行状态的线程数等于corePoolSize时,新增的任务将被放进任务队列中;
3. 当运行状态的线程数等于corePoolSize并且任务队列的大小达到queueCapacity时,新提交的任务将会创建新线程进行处理;
4. 当运行状态的线程数等于maximumPoolSize 并且任务队列大小达到queueCapacity,此时提交的任务将被拒绝;
故上述的b,c,d场景比较明确处理方式了;此时场景e该如何处理呢?
在细追代码之前,我们直接撸个代码看下运行结果:
- 任务线程代码
package com.test.chan.pools;
import java.util.Date;
public class BlockThread implements Runnable {
private String tKey = null;
private long blockTS = 0L;
public BlockThread(int blockTimes,String threadKey){
this.blockTS = blockTimes * 1000L;
this.tKey = threadKey;
}
public void run() {
System.out.printf("[%s]======== START %s - %s ===============\n", new Date(),this.tKey, this.blockTS);
try {
Thread.sleep(this.blockTS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("[%s]======== END %s - %s ===============\n", new Date(),this.tKey, this.blockTS);
}
}
- 验证代码
package com.test.chan.pools;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo {
public static void main(String[] args){
int corePoolSize = 5;
int maximumPoolSize = 15;
long keepAliveTime = 0L;
int queueCapacity = 10;
ExecutorService exec = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(queueCapacity));
//前5个正常提交,任务阻塞时间未300s
//前15个,有10个任务阻塞,任务阻塞时间10s
//前16个,任务阻塞时间10s,观察队列中的任务是否被执行
String key_pre = "STEP1-";
int taskNum = 5;
int blockTime = 300;
//STEP1. 提交5个阻塞时间为300s的任务
for(int i =0;i<taskNum;++i){
BlockThread bt = new BlockThread(blockTime,key_pre + i);
exec.execute(bt);
}
try {
Thread.sleep(1 * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
key_pre = "STEP2-";
taskNum = 10;
blockTime = 10;
//STEP2. 继续提交10个阻塞时间未10s的任务
for(int i =0;i<taskNum;++i){
BlockThread bt = new BlockThread(blockTime,key_pre + i);
exec.execute(bt);
}
try {
Thread.sleep(1 * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
key_pre = "STEP3-";
taskNum = 1;
blockTime = 10;
//STEP3. 继续提交1个阻塞时间为10s的任务
for(int i =0;i<taskNum;++i){
BlockThread bt = new BlockThread(blockTime,key_pre + i);
exec.execute(bt);
}
//SLEEP 60s
try {
Thread.sleep(60 * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
key_pre = "STEP4-";
taskNum = 20;
blockTime = 10;
//STEP4. 继续提交20个,阻塞时间未10s的任务
for(int i =0;i<taskNum;++i){
BlockThread bt = new BlockThread(blockTime,key_pre + i);
exec.execute(bt);
}
System.out.println("------------ END --------------");
}
}
- 查看输出结果并分析
/** 初始提交任务 **/
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-0 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-4 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-3 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-2 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-1 - 300000 ===============
/** 提交的任务进入任务队列中 **/
/** 任务队列满了,提交任务则将创建新线程处理该任务 **/
[Thu Aug 20 20:15:32 CST 2020]======== START STEP3-0 - 10000 ===============
[Thu Aug 20 20:15:42 CST 2020]======== END STEP3-0 - 10000 ===============
/** 完成任务的线程从任务队列中取任务执行 **/
[Thu Aug 20 20:15:42 CST 2020]======== START STEP2-0 - 10000 ===============
[Thu Aug 20 20:15:52 CST 2020]======== END STEP2-0 - 10000 ===============
[Thu Aug 20 20:15:52 CST 2020]======== START STEP2-1 - 10000 ===============
[Thu Aug 20 20:16:02 CST 2020]======== END STEP2-1 - 10000 ===============
[Thu Aug 20 20:16:02 CST 2020]======== START STEP2-2 - 10000 ===============
[Thu Aug 20 20:16:12 CST 2020]======== END STEP2-2 - 10000 ===============
[Thu Aug 20 20:16:12 CST 2020]======== START STEP2-3 - 10000 ===============
[Thu Aug 20 20:16:22 CST 2020]======== END STEP2-3 - 10000 ===============
[Thu Aug 20 20:16:22 CST 2020]======== START STEP2-4 - 10000 ===============
/** 任务队列满,新增线程处理新提交的任务 **/
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-5 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-13 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-7 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-6 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-8 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-12 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-11 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-10 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-9 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== END STEP2-4 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP2-5 - 10000 ===============
/** 任务队列满了,运行的线程数达到maximumPoolSize,新提交任务被拒绝 **/
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.test.chan.pools.BlockThread@6e0be858 rejected from java.util.concurrent.ThreadPoolExecutor@61bbe9ba[Running, pool size = 15, active threads = 15, queued tasks = 10, completed tasks = 5]
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 com.test.chan.pools.ThreadPoolDemo.main(ThreadPoolDemo.java:67)
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-13 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-6 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-6 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-7 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-5 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-12 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-0 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-11 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-8 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-2 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-9 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-10 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-4 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-8 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-7 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-3 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-1 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-9 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP2-5 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-6 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-0 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-7 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-2 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-8 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-4 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-1 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-9 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-3 - 10000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-0 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-4 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-3 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-1 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-2 - 300000 ===============
从结果回推: 场景e下,完成工作的线程将会从任务队列中取任务并执行,同时此时若新增任务,则先放入任务队列中(有空位);
即当运行状态的线程数等于corePoolSize并且任务队列的大小达到queueCapacity时,此时提交的任务将会被优先执行(新建线程来执行该任务);当任务队列中的堆积任务被处理完后,则线程池的可用线程数将逐步降低至corePoolSize(allowCoreThreadTimeOut=false)
网友评论