通过代码实现来检验以下特性:
l 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
l 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
l 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
l 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
l 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
程序设计
- 设计一个简单的Tread子类,以一定间隔输出信息。
- 使用线程池执行一系列子类,控制任务的数量以填充线程池,队列。
- 监视线程池信息。
代码
线程
//ConfigurableThread具体代码请见《简单可定制业务的线程类》
public class TaskOne extends ConfigurableThread{
private int i = 0;
private int id = 0;
public TaskOne(int id){this.id = id;}
@Override
public void doRun(){
System.out.println("线程 : " + this.id + " | 计算:"+ ++i);
this.successFlag = true;
}
}
线程池
我们设置核心线程数量为3,最大线程数量为5,队列长度为3,最大空闲时间为3s。拒绝策略依次测试。同时往线程池中填充10个任务,观察输出情况。
BlockingQueue queue = new LinkedBlockingQueue<Runnable>(3);
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 5L,TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy());
for(int i = 0; i < 10; i ++){
try {
pool.execute(new TaskOne(i).enableExeLimit().setExeLimit(3).setInterval(1000L));
Thread.sleep(10L);
} catch (Exception e) {
e.printStackTrace();
}
}
//任务填充完毕后监视线程池的活动线程数量
while(true){
try {
System.out.println("线程状态 : "+pool.getPoolSize());
Thread.sleep(500L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
拒绝策略与结果
AbortPolicy-拒绝新任务
执行结果
线程 : 0 | 计算:1
线程 : 1 | 计算:1
线程 : 2 | 计算:1
线程 : 6 | 计算:1
线程 : 7 | 计算:1
java.util.concurrent.RejectedExecutionException: Task Thread[Thread-8,5,main] rejected from java.util.concurrent.ThreadPoolExecutor@5aaa6d82[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
at app.APP.main(APP.java:21)
java.util.concurrent.RejectedExecutionException: Task Thread[Thread-9,5,main] rejected from java.util.concurrent.ThreadPoolExecutor@5aaa6d82[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
at app.APP.main(APP.java:21)
可已看出线程0,1,2依次占用3个核心线程,3,4,5放入队列。当队列放不下时,6,7放入新开的线程,此时达到最大线程数5。因此8,9任务返回拒绝异常。
CallerRunsPolicy-阻塞并持续等待
执行结果
线程 : 0 | 计算:1
线程 : 1 | 计算:1
线程 : 2 | 计算:1
线程 : 6 | 计算:1
线程 : 7 | 计算:1
线程 : 8 | 计算:1
任务执行成功,线程结束
循环结束,退出
线程 : 3 | 计算:1
任务执行成功,线程结束
循环结束,退出
线程 : 4 | 计算:1
任务执行成功,线程结束
循环结束,退出
线程 : 5 | 计算:1
任务执行成功,线程结束
循环结束,退出
//......
//一系列输出....直到有线程执行结束才开始输出状态检验
线程状态 : 5
线程状态 : 5
线程状态 : 5
//...等待3s后
线程状态 : 5
线程状态 : 5
线程状态 : 4
线程状态 : 4
线程状态 : 3
线程状态 : 3
从上边结果可以看出两个信息:
* 任务0,1,2,6,7首先执行,3,4,5队列等待,此时线程池满。代码一直等待而不往下执行线程状态监视部分。直到有任务结束,8,9也加入线程池后,代码才继续往下执行。
* 当线程数量大于核心数量时,空闲线程等待时间满足最大空闲时间后,多余的线程将被销毁。
DiscardOldestPolicy-忽略最老的任务
执行结果
线程 : 0 | 计算:1
线程 : 1 | 计算:1
线程 : 2 | 计算:1
线程 : 6 | 计算:1
线程 : 7 | 计算:1
线程状态 : 5
线程状态 : 5
任务执行成功,线程结束
循环结束,退出
线程 : 5 | 计算:1
任务执行成功,线程结束
循环结束,退出
线程 : 8 | 计算:1
任务执行成功,线程结束
循环结束,退出
线程 : 9 | 计算:1
任务执行成功,线程结束
可以看到,0,1,2,6,7最先执行,3,4,5在队列中,当8,9加入时,最老的任务3,4被忽略,5被保留。
DiscardPolicy-忽略后来的任务
执行结果
线程 : 0 | 计算:1
线程 : 1 | 计算:1
线程 : 2 | 计算:1
线程 : 6 | 计算:1
线程 : 7 | 计算:1
线程状态 : 5
线程状态 : 5
任务执行成功,线程结束
循环结束,退出
线程 : 3 | 计算:1
任务执行成功,线程结束
循环结束,退出
线程 : 4 | 计算:1
任务执行成功,线程结束
循环结束,退出
线程 : 5 | 计算:1
任务执行成功,线程结束
可以看出0,1,2,6,7最先执行,3,4,5在队列中,当8,9加入时,直接忽略8,9。因此只有前8个任务真正被执行。