「视频版」当线程池溢出以后,程序会奔溃吗?面试突击 007 期

本文的面试题是,当线程池的任务溢出以后,程序会奔溃吗?
这个问题问的是关于线程池的任务数超过线程池的承载能力以后,会出现什么状况?
那么,咱们本文就手撸模拟一个线程池溢出的状况,来看程序的执行状况。
涉及知识点java

  1. 核心线程数和最大线程数有什么区别?
  2. 如何模拟线程池溢出?
  3. 拒绝策略的执行流程是什么?
  4. 什么是线程池的拒绝策略?
  5. Java 自带的拒绝策略有哪些?
  6. 如何自定义拒绝策略?
  7. 视频面试答案

视频内容(由于视频比较大,分红了两个视频来展现 ):面试

图文面试答案ide

当线程池的任务溢出以后,程序并不会奔溃,这时候会触发线程池的拒绝策略,Java 自带的拒绝策略有四种:
AbortPolicy:终止策略,线程池终止执行,并直接抛出异常,Java 默认此拒绝策略;
CallerRunsPolicy:把任务交给当前线程执行(原本是线程池本身要执行的,结果处理不过来就交给当前的主线程去处理);
DiscardPolicy:忽略此任务(最新的任务);
DiscardOldestPolicy:忽略最先的任务(最久的任务)。
拒绝策略的执行流程比较绕,这是由于线程池有三个重要的参数:核心线程数(corePoolSize)、最大线程数(maximumPoolSize)、线程池的任务队列(BlockingQueue),大部分搞不清楚核心线程数和最大线程数有什么区别?
核心线程数是指在正常状况下线程池内的线程数量;而最大线程数指的是当线程池的任务队列存储超过最大值以后,能够建立最多的线程数量。
当任务比较少的时候,线程数量会根据设置的超时时间,回归线程的数量为核心线程数量,这个时候最大线程数就暂时没用了(没有发挥的余地了)。
拒绝策略的执行流程是:当提交的任务数量大于核心线程数时,任务会被放入到线程池的任务队列中,当任务超过了最大队列值时,判断当前线程数量是否大于最大线程数,若是小于最大线程数则会新建立线程处理次任务,相反的状况下就会执行拒绝策略,以下图所示:
「视频版」当线程池溢出以后,程序会奔溃吗?面试突击 007 期线程

模拟线程池溢出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】
近期热文队列

  • HashMap 为何会致使 CPU 100%?面试突击 006 期
  • 面试突击 005 | Redis 是如何实现高可用的?它的实现方式有哪些?
  • 面试突击 004 | 如何排查 Redis 中的慢查询?视频实战篇
  • 面试突击 003 | Redis 如何实现查询附近的人?
  • 面试突击 002 | Redis 是如何处理已过时元素的?
  • 面试突击 001 | Redis 如何从海量数据中查询出某一个 Key?
  • Java面试详解(2020版):500+ 面试题和核心知识点详解
    关注下方二维码,订阅更多精彩内容
    「视频版」当线程池溢出以后,程序会奔溃吗?面试突击 007 期
相关文章
相关标签/搜索