前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程池的拒绝策略

线程池的拒绝策略

作者头像
JavaFish
修改2021-08-04 10:45:02
6880
修改2021-08-04 10:45:02
举报
文章被收录于专栏:狗哥的 Java 世界

线程池

欢迎来到狗哥多线程系列连载。本篇是线程相关的第七篇,前六篇分别是:

创建线程到底有几种方式?

线程有多少种状态?

Runnable 一定在执行任务吗?

万字长文,Thread 类源码解析!

wait、notify/notifyAll 解析

线程之生产者消费者模式

狗哥肝了一下午的线程池

线程池的拒绝策略

因为线程池是在提交任务时根据情况执行拒绝策略的,而提交任务涉及两个方法:execute 和 sumbit。在说拒绝策略之前,先谈谈这两方法的区别。

什么时候执行拒绝策略?

先看看 submit 的源码:可以看到它最终还是调用 execute 方法。与 execute 的区别就是:可以接收线程池执行的返回值,而 execute () 不能接收返回值

代码语言:javascript
复制
public Future < ? > submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture < Void > ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

再看 execute 源码,注释写得很清楚了,简单说下,分为三步:

  • 当工作线程小于核心线程时,直接提交执行就完事了。
  • 线程池处于运行状态,提交任务到队列。再次检查状态,若非运行状态,则移除任务并执行拒绝策略;否则,创建线程执行任务。
  • 线程池处于非运行状态或者启动线程执行失败,则执行拒绝策略。
代码语言:javascript
复制
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);
}
Java 提供的拒绝策略

Java 给我们提供了拒绝策略,创建线程池时就可以指定拒绝策略,比如:

代码语言:javascript
复制
newThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue < > (),
    new ThreadPoolExecutor.AbortPolicy());

当然,你也可以自己实现~

上面了解了拒绝策略的执行时机,再来看看 Java 给我们提供的拒绝策略。分为四种,关系如下:

拒绝策略.png

逐个聊聊它们的特点:

  • AbortPolicy:线程池默认的拒绝策略(不值当就是它),拒绝任务时,直接抛出一个类型为 RejectedExecutionException 的运行时异常。你可以捕获逐个异常并根据自己的业务进行处理。
  • DiscardPolicy:这种非常粗暴,拒绝任务时不通知你。相当于,这个任务提没有提交成功,你是不知道的,非常不友好。
  • DiscardOldestPolicy:看名字就知道跟第二种差不多,一样不通知。区别在于:它抛弃的任务通常是队列的头结点,也就是存活时间最长的任务。
  • CallerRunsPolicy:这种最友好。线程池没能力执行任务,就把这个任务交于提交任务的线程执行,也就是谁提交谁执行(并不一定是主线程提交任务)。这样做有两点好处:一是新提交任务不被丢弃,不会造成数据丢失;二是因为谁提交谁执行,当任务执行耗时比较长,它也就不会去提交任务,减缓任务提交的速度,线程池中的线程可以趁机执行掉一部分任务,相当于给线程提供一个缓冲期。

总结

本文从源码分析了拒绝策略的执行时机并详细介绍了 Java 提供的四种拒绝策略,相信大家看完会有所收获。选用哪种线程池是根据你自己的业务而定的,实践出真知。

巨人的肩膀

  • https://kaiwu.lagou.com/course/courseInfo.htm?courseId=16#/detail/pc?id=239

-END-

如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「一个优秀的废人」,关注后回复「 1024」送你一套完整的 java 教程(包括视频)。回复「 电子书」送你Java、C、C++、Linux、Go、Python、数据库、前端、计算机基础、人工智能、数据结构与算法、设计模式以及面试相关电子书

教程节选

代码语言:javascript
复制
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一个优秀的废人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 线程池
    • 线程池的拒绝策略
      • 什么时候执行拒绝策略?
      • Java 提供的拒绝策略
    • 总结
      • 巨人的肩膀
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档