问:如果线程池中所有的线程都被占用且工作队列都填满的情况下,你们系统的线程池对新进来的任务是怎么处理的?
/**
* 1.corePoolSize:指定线程池中活跃的线程数量
* 2.maximumPoolSize:指定线程池中最大线程数量
* 3.keepAliveTime:超过corePoolSize个多余线程的存活时间
* 4.unit:keepAliveTime的时间单位 TimeUnit
* 5.workQueue:任务队列,被提交但尚未被执行的任务
* 6.threadFactory:线程工厂,用于创建线程
* 7.handler拒绝策略:当任务太多来不及处理时,如何拒绝任务
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0){
throw new IllegalArgumentException();
}
if (workQueue == null || threadFactory == null || handler == null){
throw new NullPointerException();
}
this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
该题考的是对常用线程池的拒绝策略的深度理解:
线程池的拒绝策略有四种:
AbortPolicy(中止),
CallerRunsPolicy(调用者运行),
DiscardPolicy(中止不抛出异常),
DiscardOldestPolicy([删除队列中旧任务执行新任务]喜新厌旧)
上面四种策略根据业务选择不同的使用方式,比如我们业务支付系统使用的是CallerRunsPolicy策略
...
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
...
中止(Abort)策略是默认的饱和策略,该策略将会抛出未检查的RejectExecutionException异常,调用者可以捕获该异常,然后根据自己的需要编写处理业务逻辑代码。
抛弃(Discard)策略就是当新提交的任务无法保存到队列中等待执行时,"抛弃(Discard)"策略会悄悄抛弃该任务,不抛出任何异常。
抛弃最旧(DiscardOldest)策略将会抛弃下一个将被执行的任务,然后尝试重新提交新的任务,如果工作队列是一个优先队列,那么将抛弃最旧的策略将抛弃优先级最高的任务。
因此最好不要将"抛弃最旧的"饱和策略和优先级队列放在一起使用。
调用者运行(CallerRuns)策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退给调用者,从而降低新任务的流量。
它不会在线程池的某个线程中执行新提交的任务,而是在一个调用了execute的线程中执行该任务。
比如我们常用的WebServer调用请求修改为使用有界队列和调用者运行的饱和策略,当线程池中的所有线程都被占用,并且工作队列被填满后,下一个任务会调用execute时在主线程中执行。
由于执行任务需要一定的时间,因此主线程至少在一段时间内不能提交任何任务,从而使得工作者线程有时间来处理完正在执行的任务。
在这期间,主线程不会调用 accept,因此到达的请求将被保存在网络TCP层的队列中而不是在应用程序的队列中。
如果请求流量还是持续不断的过来,这个时候回导致服务器过载,这种过载情况会逐渐向外蔓延开---从线程池到工作队列再到应用程序再到网络的TCP层,最终到达客户端,导致服务器在高负载下实现一种平缓的性能降低。从而保护线上服务。
public static void main(String[] args) {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
ThreadFactory factory = r -> new Thread(r, "caller-Runner线程执行");
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 4,
0L, TimeUnit.SECONDS, queue, factory,
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 1000; i++) {
int n = i;
threadPoolExecutor.submit(() -> {
try {
System.out.println("当前i=" + n + ", " + Thread.currentThread().getName() + "线程任务执行," + queue.size());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
本文共计 4541 字,感谢您的耐心浏览与评论。
0条回应:“问:如果线程池中所有的线程都被占用且工作队列都填满的情况下,你们系统的线程池对新进来的任务是怎么处理的?”