本文的面试题是,当线程池的任务溢出以后,程序会奔溃吗?
这个问题问的是关于线程池的任务数超过线程池的承载能力以后,会出现什么状况?
那么,咱们本文就手撸模拟一个线程池溢出的状况,来看程序的执行状况。
涉及知识点java
视频内容(由于视频比较大,分红了两个视频来展现 ):面试
图文面试答案ide
当线程池的任务溢出以后,程序并不会奔溃,这时候会触发线程池的拒绝策略,Java 自带的拒绝策略有四种:
AbortPolicy:终止策略,线程池终止执行,并直接抛出异常,Java 默认此拒绝策略;
CallerRunsPolicy:把任务交给当前线程执行(原本是线程池本身要执行的,结果处理不过来就交给当前的主线程去处理);
DiscardPolicy:忽略此任务(最新的任务);
DiscardOldestPolicy:忽略最先的任务(最久的任务)。
拒绝策略的执行流程比较绕,这是由于线程池有三个重要的参数:核心线程数(corePoolSize)、最大线程数(maximumPoolSize)、线程池的任务队列(BlockingQueue),大部分搞不清楚核心线程数和最大线程数有什么区别?
核心线程数是指在正常状况下线程池内的线程数量;而最大线程数指的是当线程池的任务队列存储超过最大值以后,能够建立最多的线程数量。
当任务比较少的时候,线程数量会根据设置的超时时间,回归线程的数量为核心线程数量,这个时候最大线程数就暂时没用了(没有发挥的余地了)。
拒绝策略的执行流程是:当提交的任务数量大于核心线程数时,任务会被放入到线程池的任务队列中,当任务超过了最大队列值时,判断当前线程数量是否大于最大线程数,若是小于最大线程数则会新建立线程处理次任务,相反的状况下就会执行拒绝策略,以下图所示:线程
模拟线程池溢出code
public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 6; i++) { executor.execute(() -> { System.out.println(Thread.currentThread().getName()); }); } } 程序的执行结果以下: pool-1-thread-2 pool-1-thread-2 pool-1-thread-1 pool-1-thread-3 pool-1-thread-4 Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task org.example.App$$Lambda$1/1096979270@7cca494b rejected from java.util.concurrent.ThreadPoolExecutor@7ba4f24f[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0] 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 org.example.App.main(App.java:13)
从执行结果能够看出,循环在执行第 6 次就抛出异常了,这是由于最大线程数为 4,而队列最大只能存储 1 个任务,因此在第 6 个任务过来的时候,线程池已经超负荷运行了,只能执行拒绝策略了,而咱们设置的拒绝策略是 AbortPolicy 因此会抛出异常。
自定义拒绝策略视频
除了 Java 自带的四种拒绝策略外,咱们还能够自定义拒绝策略,代码以下:blog
public static void main(String[] args) throws InterruptedException { ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { // 添加业务处理代码 System.out.println("自定义拒绝策略"); } }); for (int i = 0; i < 7; i++) { executor.execute(() -> { System.out.println(Thread.currentThread().getName()); }); } }
以上程序执行结果以下:
自定义拒绝策略
自定义拒绝策略
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
能够看出自定义拒绝策略,只须要重写 RejectedExecutionHandler 接口的 rejectedExecution 方法便可,能够在此方法中添加本身的业务处理代码。
小结接口
本文讲了线程池任务新增时的执行流程,先判断是否有空闲线程,若是的话直接执行任务,若是没有的话再判断任务队列是不是否饱和,若是不饱和把任务存储到队列中,若是饱和须要判断当前线程数是否大于最大线程数,若是小于则新增线程执行此任务,反之则执行拒绝策略。Java 提供了四种拒绝策略,你能够经过重写 RejectedExecutionHandler 接口来自定义拒绝策略。
更多执行细节和更多知识点说明,详见本文的视频部分。
【END】
近期热文队列