public class ThreadPoolTest {
public static void main(String[] args) {
//核心线程数
int corePoolSize = 3;
//最大线程数
int maxPoolSize = 6;
//超过核心线程数量的线程最大空闲时间
long keepAliveTime = 2;
//时间单位秒
TimeUnit unit = TimeUnit.SECONDS;
//创建工作队列,用于存放提交的等待执行任务
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(2);
ThreadPoolExecutor threadPoolExecutor = null;
try {
//核心线程数,最大线程数,超过核心线程数线程空闲时间,单位,工作队列,拒绝策略
threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,unit,blockingQueue,new ThreadPoolExecutor.AbortPolicy());
//提交任务
for (int i = 0; i < 8; i++) {
int index = i+1;
threadPoolExecutor.submit(() -> {
System.out.println("线程:" + index);
//模拟线程执行时间
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//线程提交完,线程睡一觉
Thread.sleep(50);
}
}catch (Exception e){
e.printStackTrace();
}
}
/*
运行结果分析:
1.ThreadPool构造函数创建线程池
2.for循环提交8个任务,(等于 最大线程数 + 队列等待数)
3.通过threadPoolExecutor.submit 提交Runnable接口任务
4.提交 1 的时候,由于当前线程正在执行的任务是0小于核心线程数(3),
所以会新建一个线程来提交任务1
5.提交2,3时候 当前正在执行的任务 小于等于核心线程数(3)
所以都会新建一个线程来提交任务2,3
6.当前线程池中正在执行的线程是3(线程执行是10s,所以3个新建的线程都会在执行)
提交4的时候,如果新建线程来执行,执行的线程数就大于了核心线程数所以不能再新建线程,
此时会把 任务4 放到队列等待
7.队列的大小是2 任务5 也会放入队列等待
8.提交任务6,此时队列已经满了,另外核心线程可执行线程数是3,这时就要判断线程池中
执行的任务数是不是大于最大线程数(6)
9.如果小于6,那么就会新建一个线程来执行任务6
10.执行7,8的时候也要判断是不是大于最大线程数(6)
11.此时6个任务都有线程在执行,那么队列中的任务 4,5什么时候执行
12.当任务1执行完毕(10s后),线程1不会被销毁,而是获取队列中的任务4来执行
13.当任务2执行完毕(10s后),线程2不会被销毁,而是获取队列中的任务5来执行
上述流程可知:线程池中的线程执行完毕后不会立刻销毁,线程池中会保留核心线程数的线程,
当队列中有任务等待或是新提交的任务,那么会通过线程池中已有的线程来执行任务,避免了
线程频繁的创建和销毁,而大于核心线程数 小于最大线程数 创建的线程会在空闲时间后被回收
线程池的拒绝策略:当线程池关闭或饱和的时候(达到最大线程数 队列已满),新提交的任务会被拒绝
1.AbortPolicy(默认拒绝策略):拒绝任务时抛出异常 RejectedExecutionException
2.CallerRunsPolicy:将被拒绝任务添加到线程池正在运行的线程中去执行,如果线程池关闭,任务被丢弃
3.DiscardPolicy:丢弃任务但不抛出异常
4.DiscardOldestPolicy:丢弃队列中等待时间最长的任务,并执行当前提交的任务。如果线程池关闭,认为丢弃
线程池队列入队策略:
1.直接传递:通过 SynchronousQueue 直接把任务传递给线程。
如果当前没可用线程,尝试入队操作会失败,然后再创建一个新的线程。
当处理可能具有内部依赖性的请求时,该策略会避免请求被锁定。
直接传递通常需要无界的最大线程数(maximumPoolSize),避免拒绝新提交的任务。
当任务持续到达的平均速度超过可处理的速度时,可能导致线程的无限增长。
2.有界队列:当使用有限的最大线程数时,有界队列(如 ArrayBlockingQueue)可以防止资源耗尽,但是难以调整和控制。
队列大小和线程池大小可以相互作用:使用大的队列和小的线程数可以减少CPU使用率、系统资源和上下文切换的开销,但是会导致吞吐量变低,
如果任务频繁地阻塞(例如被I/O限制),系统就能为更多的线程调度执行时间。
使用小的队列通常需要更多的线程数,这样可以最大化CPU使用率,但可能会需要更大的调度开销,从而降低吞吐量。
3.无界队列:使用无界队列(如 LinkedBlockingQueue)作为等待队列,当所有的核心线程都在处理任务时, 新提交的任务都会进入队列等待。
因此,不会有大于 corePoolSize 的线程会被创建(maximumPoolSize 也将失去作用)。
这种策略适合每个任务都完全独立于其他任务的情况;例如网站服务器。这种类型的等待队列可以使瞬间爆发的高频请求变得平滑。
当任务持续到达的平均速度超过可处理速度时,可能导致等待队列无限增长。
*/
}
运行结果: