Java线程池的拒绝策略
一、简介
jdk1.5 版本新增了JUC并发编程包,极大的简化了传统的多线程开发。前面文章中介绍了线程池的使用,链接地址:https://www.cnblogs.com/eric-fang/p/9004020.html
Java线程池,是典型的池化思想的产物,类似的还有数据库的连接池、redis的连接池等。池化思想,就是在初始的时候去申请资源,创建一批可使用的连接,这样在使用的时候,就不必再进行创建连接信息的开销了。举个生活中鲜明的例子,在去著名洋快餐某基或者某劳的时候,配餐人员是从一个中间的保温箱中直接取食材,然后打包就好了。不用再临时的来了一个单子,又要去拿原材料,又要去进行加工。效率明显的就是提高了很多。
俗话说 满而不损则溢,盈而不持则倾。线程池既然是容器,那么必然的会有存满的情况。在达到某些特定条件的时候,再来请求的话,池子是如何进行请求处理的呢?这里就引出了池的拒绝策略。一般的数据库连接池在达到最大连接数的时候会默认的等待特定的设置的时间或者直接就抛出异常。而本文中要阐述的线程池却并非如此的策略,下面开始展开讲解下。
二、线程池的拒绝策略
线程池中,有三个重要的参数,决定影响了拒绝策略:corePoolSize - 核心线程数,也即最小的线程数。workQueue - 阻塞队列 。 maximumPoolSize - 最大线程数
当提交任务数大于 corePoolSize 的时候,会优先将任务放到 workQueue 阻塞队列中。当阻塞队列饱和后,会扩充线程池中线程数,直到达到 maximumPoolSize 最大线程数配置。此时,再多余的任务,则会触发线程池的拒绝策略了。
总结起来,也就是一句话,当提交的任务数大于(workQueue.size() + maximumPoolSize ),就会触发线程池的拒绝策略。
三、拒绝策略定义
拒绝策略提供顶级接口 RejectedExecutionHandler ,其中方法 rejectedExecution 即定制具体的拒绝策略的执行逻辑。
jdk默认提供了四种拒绝策略:
CallerRunsPolicy - 当触发拒绝策略,只要线程池没有关闭的话,则使用调用线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
AbortPolicy - 丢弃任务,并抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
DiscardPolicy - 直接丢弃,其他啥都没有
DiscardOldestPolicy - 当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入
四、测试代码
1、AbortPolicy
package com.cfang; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @Slf4j
public class T2 { public static void main(String[] args) throws Exception{
int corePoolSize = ;
int maximumPoolSize = ;
long keepAliveTime = ;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, handler);
for(int i=; i<; i++) {
try {
executor.execute(new Thread(() -> log.info(Thread.currentThread().getName() + " is running")));
} catch (Exception e) {
log.error(e.getMessage());
}
}
executor.shutdown();
}
}
executor.execute()提交任务,由于会抛出 RuntimeException,如果没有try.catch处理异常信息的话,会中断调用者的处理流程,后续任务得不到执行(跑不完100个)。可自行测试下,很容易在控制台console中能查看到。
2、CallerRunsPolicy
主体代码同上,更换拒绝策略:
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
运行后,在控制台console中能够看到的是,会有一部分的数据打印,显示的是 “main is running”,也即体现调用线程处理。
3、DiscardPolicy
更换拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
直接丢弃任务,实际运行中,打印出的信息不会有100条。
4、DiscardOldestPolicy
同样的,更换拒绝策略:
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
实际运行,打印出的信息也会少于100条。
五、总结
四种拒绝策略是相互独立无关的,选择何种策略去执行,还得结合具体的业务场景。实际工作中,一般直接使用 ExecutorService 的时候,都是使用的默认的 defaultHandler ,也即 AbortPolicy 策略。
Java线程池的拒绝策略的更多相关文章
- JDK线程池的拒绝策略
关于新疆服务请求未带入来话原因的问题 经核查,该问题是由于立单接口内部没有成功调用接续的 “更新来电原因接口”导致的,接续测更新来电原因接口编码:NGCCT_UPDATESRFLAG_PUT ,立单接 ...
- SimpleThreadPool给线程池增加拒绝策略和停止方法
给线程池增加拒绝策略和停止方法 package com.dwz.concurrency.chapter13; import java.util.ArrayList; import java.util. ...
- Dubbo里面线程池的拒绝策略
Dubbo里面线程池的拒绝策略 public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { protecte ...
- dubbo线程池的拒绝策略
jdk自带的原生的拒绝策略抛出的异常信息不够详细,而dubbo对拒绝策略进行了改写,抛出的信息更具有参考价值,值得我们借鉴. jdk自带的原生拒绝策略抛出的信息: // ThreadPoolExecu ...
- Java - "JUC线程池" 线程状态与拒绝策略源码分析
Java多线程系列--“JUC线程池”04之 线程池原理(三) 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基本概念"中,我们介绍过,线程有5种状态:新建 ...
- Java 线程池 8 大拒绝策略,面试必问!
前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...
- java并发:线程池、饱和策略、定制、扩展
一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...
- 面试题-关于Java线程池一篇文章就够了
在Java面试中,线程池相关知识,虽不能说是必问提,但出现的频次也是非常高的.同时又鉴于公众号"程序新视界"的读者后台留言让写一篇关于Java线程池的文章,于是就有本篇内容,本篇将 ...
- JUC(4)---java线程池原理及源码分析
线程池,既然是个池子里面肯定就装很多线程. 如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁 线程,如此一来会大大降低系统的效率.可能出现服务器在为每个请求创建新线程和销毁 ...
随机推荐
- Flink的TaskManager启动(源码分析)
通过启动脚本已经找到了TaskManager 的启动类org.apache.flink.runtime.taskexecutor.TaskManagerRunner 来看一下它的main方法中 最后被 ...
- Redis学习总结(九)-- Redis常用技巧
这里会记录下Redis 常用的小技巧 全局使用 redis-cli 等命令 在之前我们都在做 Redis 命令目录下通过 ./redis-cli这种形式访问,如果使用 redis-cli 的话就会报命 ...
- MyBatis 返回 Map 字段丢失问题
问题现象 执行存储过程返回 Map 集合数据,发现有字段丢失情况,仔细研究发现丢失的字段值都为 NULL. 解决办法1: 在查询 SQL 语句中增加 NULL 判断函数 MSSQL: isnull(字 ...
- 04_枚举类型iota
iota是枚举类型的关键字,使用iota可以方便快捷的给常量赋值,主要体现在以下几个方面:1.iota常量自动生成器,每个一行加12.iota给常量赋值使用3.iota遇到const重置为04.可以写 ...
- 深入剖析PHP7内核源码(二)- PHP变量容器
简介 PHP的变量使用起来非常方便,其基本结构是底层实现的zval,PHP7采用了全新的zval,由此带来了非常大的性能提升,本文重点分析PHP7的zval的改变. PHP5时代的ZVAL typed ...
- 【Leetcode】【简单】【189. 旋转数组】【JavaScript】
189. 旋转数组 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: [1,2,3,4,5,6,7] 和 k = 3输出: [5,6,7,1,2,3,4]解释 ...
- 朋友聚会,下馆子要到哪家饭馆?——单样本T检验帮你找到答案
聚会时,五花八门的饭馆让人眼花缭乱,应该到哪家店吃呢?除了美味的食物,良好的服务态度也是好饭馆的必备品质,如何判断一家饭馆的服务态度如何?此时可以用单样本T检验来找答案~ 让顾客对A饭馆的服务态度 ...
- [翻译] C# 8.0 接口默认实现
原文: Default implementations in interfaces 随着上周的 .NET Core 3.0 Prview 5 和 Visual Studio 2019 version ...
- Reactive(2) 响应式流与制奶厂业务
目录 再谈响应式 为什么Web后端开发的,对 Reactive 没有感觉 Java 9 支持的 Reactive Stream 范例 小结 扩展阅读 再谈响应式 在前一篇文章从Reactive编程到& ...
- pickle 都写文件
import pickle mylist=[[1,2,3,4,5,6,7],["abc","xyz","hello"],[1,2,3,4,5 ...