当前位置:首页 > 科技  > 软件

面试必问:线程池是如何执行的?它的拒绝策略有哪些?

来源: 责编: 时间:2023-08-20 23:17:03 448观看
导读聊到线程池就一定会聊到线程池的执行流程,也就是当有一个任务进入线程池之后,线程池是如何执行的?我们今天就来聊聊这个话题。线程池是如何执行的?线程池的拒绝策略有哪些?线程池执行流程想要真正的了解线程池的执行流程,就

bA528资讯网——每日最新资讯28at.com

聊到线程池就一定会聊到线程池的执行流程,也就是当有一个任务进入线程池之后,线程池是如何执行的?我们今天就来聊聊这个话题。线程池是如何执行的?线程池的拒绝策略有哪些?bA528资讯网——每日最新资讯28at.com

线程池执行流程

想要真正的了解线程池的执行流程,就得先从线程池的执行方法 execute() 说起,execute() 实现源码如下:bA528资讯网——每日最新资讯28at.com

public void execute(Runnable command) {    if (command == null)        throw new NullPointerException();    int c = ctl.get();    // 当前工作的线程数小于核心线程数    if (workerCountOf(c) < corePoolSize) {        // 创建新的线程执行此任务        if (addWorker(command, true))            return;        c = ctl.get();    }    // 检查线程池是否处于运行状态,如果是则把任务添加到队列    if (isRunning(c) && workQueue.offer(command)) {        int recheck = ctl.get();        // 再次检线程池是否处于运行状态,防止在第一次校验通过后线程池关闭        // 如果是非运行状态,则将刚加入队列的任务移除        if (! isRunning(recheck) && remove(command))            reject(command);        // 如果线程池的线程数为 0 时(当 corePoolSize 设置为 0 时会发生)        else if (workerCountOf(recheck) == 0)            addWorker(null, false); // 新建线程执行任务    }    // 核心线程都在忙且队列都已爆满,尝试新启动一个线程执行失败    else if (!addWorker(command, false))         // 执行拒绝策略        reject(command);}

从上述源码我们可以看出,当任务来了之后,线程池的执行流程是:先判断当前线程数是否大于核心线程数?如果结果为 false,则新建线程并执行任务;如果结果为 true,则判断任务队列是否已满?如果结果为 false,则把任务添加到任务队列中等待线程执行,否则则判断当前线程数量是否超过最大线程数?如果结果为 false,则新建线程执行此任务,否则将执行线程池的拒绝策略,如下图所示:bA528资讯网——每日最新资讯28at.com

bA528资讯网——每日最新资讯28at.com

线程池拒绝策略

当任务过多且线程池的任务队列已满时,此时就会执行线程池的拒绝策略,线程池的拒绝策略默认有以下 4 种:bA528资讯网——每日最新资讯28at.com

  1. AbortPolicy:中止策略,线程池会抛出异常并中止执行此任务。
  2. CallerRunsPolicy:把任务交给添加此任务的(main)线程来执行。
  3. DiscardPolicy:忽略此任务,忽略最新的一个任务。
  4. DiscardOldestPolicy:忽略最早的任务,最先加入队列的任务。

默认的拒绝策略为 AbortPolicy 中止策略。bA528资讯网——每日最新资讯28at.com

DiscardPolicy拒绝策略

接下来我们以 DiscardPolicy 忽略此任务,忽略最新的一个任务为例,演示一下拒绝策略的具体使用,实现代码如下:bA528资讯网——每日最新资讯28at.com

public static void main(String[] args) {    // 任务的具体方法    Runnable runnable = new Runnable() {        @Override        public void run() {            System.out.println("当前任务被执行,执行时间:" + new Date() +                               " 执行线程:" + Thread.currentThread().getName());            try {                // 等待 1s                TimeUnit.SECONDS.sleep(1);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    };    // 创建线程,线程的任务队列的长度为 1    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,                                                           100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),                                                           new ThreadPoolExecutor.DiscardPolicy());    // 添加并执行 4 个任务    threadPool.execute(runnable);    threadPool.execute(runnable);    threadPool.execute(runnable);    threadPool.execute(runnable);    // 线程池执行完任务,关闭线程池    threadPool.shutdown();}

以上程序的执行结果如下:bA528资讯网——每日最新资讯28at.com

bA528资讯网——每日最新资讯28at.com

从上述执行结果可以看出,给线程池添加了 4 个任务,而线程池只执行了 2 个任务就结束了,其他两个任务执行了拒绝策略 DiscardPolicy 被忽略了,这就是拒绝策略的作用。bA528资讯网——每日最新资讯28at.com

AbortPolicy拒绝策略

为了和 DiscardPolicy 拒绝策略对比,我们来演示一下 JDK 默认的拒绝策略 AbortPolicy 中止策略,线程池会抛出异常并中止执行此任务,示例代码如下:bA528资讯网——每日最新资讯28at.com

public static void main(String[] args) {    // 任务的具体方法    Runnable runnable = new Runnable() {        @Override        public void run() {            System.out.println("当前任务被执行,执行时间:" + new Date() +                               " 执行线程:" + Thread.currentThread().getName());            try {                // 等待 1s                TimeUnit.SECONDS.sleep(1);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    };    // 创建线程,线程的任务队列的长度为 1    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,                                                           100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),                                                           new ThreadPoolExecutor.AbortPolicy()); // 显式指定拒绝策略,也可以忽略此设置,它为默认拒绝策略    // 添加并执行 4 个任务    threadPool.execute(runnable);    threadPool.execute(runnable);    threadPool.execute(runnable);    threadPool.execute(runnable);    // 线程池执行完任务,关闭线程池    threadPool.shutdown();}

以上程序的执行结果如下:bA528资讯网——每日最新资讯28at.com

bA528资讯网——每日最新资讯28at.com

从结果可以看出,给线程池添加了 4 个任务,线程池正常执行了 2 个任务,其他两个任务执行了中止策略,并抛出了拒绝执行的异常 RejectedExecutionException。bA528资讯网——每日最新资讯28at.com

自定义拒绝策略

当然除了 JDK 提供的四种拒绝策略之外,我们还可以实现通过 new RejectedExecutionHandler,并重写 rejectedExecution 方法来实现自定义拒绝策略,实现代码如下:bA528资讯网——每日最新资讯28at.com

public static void main(String[] args) {    // 任务的具体方法    Runnable runnable = new Runnable() {        @Override        public void run() {            System.out.println("当前任务被执行,执行时间:" + new Date() +                               " 执行线程:" + Thread.currentThread().getName());            try {                // 等待 1s                TimeUnit.SECONDS.sleep(1);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    };    // 创建线程,线程的任务队列的长度为 1    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,                                                           100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),                                                           new RejectedExecutionHandler() {                                                               @Override                                                               public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {                                                                   // 执行自定义拒绝策略的相关操作                                                                   System.out.println("我是自定义拒绝策略~");                                                               }                                                           });    // 添加并执行 4 个任务    threadPool.execute(runnable);    threadPool.execute(runnable);    threadPool.execute(runnable);    threadPool.execute(runnable);}

以上程序的执行结果如下:bA528资讯网——每日最新资讯28at.com

bA528资讯网——每日最新资讯28at.com

小结

线程池的执行流程有 3 个重要的判断点(判断顺序依次往后):判断当前线程数和核心线程数、判断当前任务队列是否已满、判断当前线程数是否已达到最大线程数。如果经过以上 3 个判断,得到的结果都会 true,则会执行线程池的拒绝策略。JDK 提供了 4 种拒绝策略,我们还可以通过 new RejectedExecutionHandler 并重写 rejectedExecution 方法来实现自定义拒绝策略。bA528资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-6174-0.html面试必问:线程池是如何执行的?它的拒绝策略有哪些?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 六种在 React 中获取数据的方法

下一篇: 揭穿DevOps的5个谣言!

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • 6月iOS设备好评榜:第一蝉联榜首近一年

    6月iOS设备好评榜:第一蝉联榜首近一年

    作为安兔兔各种榜单里变化最小的那个,2023年6月的iOS好评榜和上个月相比没有任何排名上的变化,仅仅是部分设备好评率的下降,长年累月的用户评价和逐渐退出市场的老款机器让这
  • 六大权益!华为8月服务日开启:手机免费贴膜、维修免人工费

    六大权益!华为8月服务日开启:手机免费贴膜、维修免人工费

    8月5日消息,一年一度的华为开发者大会2023(Together)日前在松山湖拉开帷幕,与此同时,华为8月服务日也式开启,到店可享六大专属权益。华为用户可在华为商城Ap
  • 线程通讯的三种方法!通俗易懂

    线程通讯的三种方法!通俗易懂

    线程通信是指多个线程之间通过某种机制进行协调和交互,例如,线程等待和通知机制就是线程通讯的主要手段之一。 在 Java 中,线程等待和通知的实现手段有以下几种方式:Object 类下
  • Golang 中的 io 包详解:组合接口

    Golang 中的 io 包详解:组合接口

    io.ReadWriter// ReadWriter is the interface that groups the basic Read and Write methods.type ReadWriter interface { Reader Writer}是对Reader和Writer接口的组合,
  • 从零到英雄:高并发与性能优化的神奇之旅

    从零到英雄:高并发与性能优化的神奇之旅

    作者 | 波哥审校 | 重楼作为公司的架构师或者程序员,你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢?笔者在出道那会为此是吃尽了苦头的,不过也得
  • 签约井川里予、何丹彤,单视频点赞近千万,MCN黑马永恒文希快速崛起!

    签约井川里予、何丹彤,单视频点赞近千万,MCN黑马永恒文希快速崛起!

    来源:视听观察永恒文希传媒作为一家MCN公司,说起它的名字来,可能大家会觉得有点儿陌生,但是说出来下面一串的名字之后,或许大家就会感到震惊,原来这么多网红,都签约这家公司了。根
  • 2纳米决战2025

    2纳米决战2025

    集微网报道 从三强争霸到四雄逐鹿,2nm的厮杀声已然隐约传来。无论是老牌劲旅台积电、三星,还是誓言重回先进制程领先地位的英特尔,甚至初成立不久的新
  • 三星Galaxy Z Fold5今日亮相:厚度缩减但仍略显厚重

    三星Galaxy Z Fold5今日亮相:厚度缩减但仍略显厚重

    据官方此前宣布,三星将于7月26日也就是今天在韩国首尔举办Unpacked活动,届时将带来带来包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy
Top