梳理总结线程池知识(内含常用线程池选型技巧及其API分类讲解)

2023-11-05

 核心、救急线程概念

核心线程

救急线程

二者区别

原始线程池的构造方法 

常用线程池及其适用场景

FixedThreadPool:

CachedThreadPool:

SingleThreadExecutor:

ScheduledExecutor:

执行单次的延时任务

周期性循环执行任务

线程池常用API

提交任务

关闭线程池 

shutdown

shutdownNow

其它方法

正确处理执行任务异常

主动捉异常 

使用 Future

核心、救急线程概念

线程池中的线程分为核心线程救急线程两种

        当阻塞队列已满并且所有核心线程都在工作的时候,再来一个任务并不会直接走拒绝策略,而是先上救急线程处理这个孤儿任务,如果没有救急线程可以救他,那么该任务就会走拒绝策略(前提是该阻塞队列是有界的!!!)


核心线程

        线程池中始终保持活动的线程数量。即使核心线程处于空闲状态,它们也不会被销毁,以便能够快速响应任务的到来。核心线程数量可以通过线程池的corePoolSize参数来设置。


救急线程

        线程池中能够创建的最大线程数量。当线程池中的任务队列已满且其余核心线程都在执行任务时,线程池会创建新的线程来处理任务,直到达到最大线程数。救急线程数量可以通过线程池的maximumPoolSize参数来设置。


二者区别

核心线程和救急线程的区别在于:

  •         核心线程始终保持活动状态,不会被销毁,以便能够快速响应任务的到来;而救急线程是在任务队列已满且核心线程都在执行任务时才会创建的,任务处理完后,在自定义的时间后,空闲的救急线程会被销毁。
  •          核心线程数量是线程池中始终存在的线程数量,而救急线程数量是线程池中能够创建的最大线程数量。(线程池大小 - 核心线程数)


原始线程池的构造方法 


常用线程池及其适用场景

根据线程池的构造方法,JDK Executors 类中提供了众多工厂方法来创建各种用途的线程池

(主要是因为构造方法太繁杂,所以说给你一些类似于枚举对象的模式来挑选大方向的类别,自己再自定义决定部分参数)


FixedThreadPool:

public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, 
                                   TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
}


CachedThreadPool:

public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, 
                                   Integer.MAX_VALUE,
                                   60L,
                                   TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
}


SingleThreadExecutor:

public static ExecutorService newSingleThreadExecutor() {
     return new FinalizableDelegatedExecutorService
                         (new ThreadPoolExecutor(1, 1,
                         0L, TimeUnit.MILLISECONDS,
                         new LinkedBlockingQueue<Runnable>()));
}


ScheduledExecutor:

        在『任务调度线程池 ScheduledExecutor 』功能加入之前,可以使用 java.util.Timer 来实现定时功能, Timer 的优点在于简单易用,但由于 所有任务都是由同一个线程来调度 ,因此所有任务都是 串行执行 的,同一时间只能有一个任务在执行,前一个 任务的延迟或异常都将会影响到之后的任务
        ScheduledExecutor相较于 Timer 的优势在于,它可以通过设置线程池的大小来决定处理任务的消费者个数,并且任务链路上的某个节点抛出异常 不会影响到后续任务。

执行单次的延时任务

ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
// 添加两个任务,希望它们都在 1s 后执行
executor.schedule(() -> {
     System.out.println("任务1,执行时间:" + new Date());
     try { Thread.sleep(2000); } catch (InterruptedException e) { }
}, 1000, TimeUnit.MILLISECONDS);

executor.schedule(() -> {
     System.out.println("任务2,执行时间:" + new Date());
}, 1000, TimeUnit.MILLISECONDS);

周期性循环执行任务

// 1s延时后开始进行周期为1s的循环执行该任务
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
log.debug("start...");
pool.scheduleAtFixedRate(() -> {
     log.debug("running...");
}, 1, 1, TimeUnit.SECONDS); // 第一个1表示延时时间   第二个1表示循环周期时间


线程池常用API

提交任务

(生产者将任务丢到队列中,让线程池的线程消费者拿去消费)


关闭线程池 

shutdown

/*
线程池状态变为 SHUTDOWN
- 不会接收新任务
- 但已提交任务会执行完
- 此方法不会阻塞调用线程的执行
*/
void shutdown();
public void shutdown() {
     final ReentrantLock mainLock = this.mainLock;
     mainLock.lock();
     try {
         checkShutdownAccess();
         // 修改线程池状态
         advanceRunState(SHUTDOWN);
         // 仅会打断空闲线程
         interruptIdleWorkers();
         onShutdown(); // 扩展点 ScheduledThreadPoolExecutor
     } finally {
         mainLock.unlock();
     }
     // 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)
     tryTerminate();
}

shutdownNow

/*
线程池状态变为 STOP
- 不会接收新任务
- 会将队列中的任务返回
- 并用 interrupt 的方式中断正在执行的任务
*/
List<Runnable> shutdownNow();
public List<Runnable> shutdownNow() {
     List<Runnable> tasks;
     final ReentrantLock mainLock = this.mainLock;
     mainLock.lock();
     try {
         checkShutdownAccess();
         // 修改线程池状态
         advanceRunState(STOP);
         // 打断所有线程
         interruptWorkers();
         // 获取队列中剩余任务
         tasks = drainQueue();
     } finally {
         mainLock.unlock();
     }
     // 尝试终结
     tryTerminate();
     return tasks;
}

其它方法

// 不在 RUNNING 状态的线程池,此方法就返回 true
boolean isShutdown();
// 线程池状态是否是 TERMINATED
boolean isTerminated();
// 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果它想在线程池 TERMINATED 后做些事情,可以利用此方法等待
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

正确处理执行任务异常

ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
     log.debug("task1");
     int i = 1 / 0;
   }
});

        如上图,当我们执行到 “int i = 1 / 0;” 代码的时候,正常来说在控制台会打印出异常信息,但是由于在使用ExecutorService执行任务时,如果任务中出现异常,并且没有在任务中进行捕获和处理,异常信息将会被线程池内部捕获并记录。这样做是为了防止异常在任务线程中抛出后终止线程的执行,从而影响线程池的正常运行。

        默认情况下,线程池内部会使用java.util.logging记录异常信息,而不是直接将异常信息打印到控制台。如果你想要在控制台上看到异常信息,可以参考如下方法

主动捉异常 

ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
     try {
         log.debug("task1");
         int i = 1 / 0;
     } catch (Exception e) {
         log.error("error:", e);
     }
});
输出
21:59:04.558 c.TestTimer [pool-1-thread-1] - task1 
21:59:04.562 c.TestTimer [pool-1-thread-1] - error: 
java.lang.ArithmeticException: / by zero 
 at cn.itcast.n8.TestTimer.lambda$main$0(TestTimer.java:28) 
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
 at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 
 at java.lang.Thread.run(Thread.java:748)

使用 Future

ExecutorService pool = Executors.newFixedThreadPool(1);
Future<Boolean> f = pool.submit(() -> {
     log.debug("task1");
     int i = 1 / 0;
     return true;
});
log.debug("result:{}", f.get());
输出
21:54:58.208 c.TestTimer [pool-1-thread-1] - task1 
Exception in thread "main" java.util.concurrent.ExecutionException: 
java.lang.ArithmeticException: / by zero 
 at java.util.concurrent.FutureTask.report(FutureTask.java:122) 
 at java.util.concurrent.FutureTask.get(FutureTask.java:192) 
 at cn.itcast.n8.TestTimer.main(TestTimer.java:31) 
Caused by: java.lang.ArithmeticException: / by zero 
 at cn.itcast.n8.TestTimer.lambda$main$0(TestTimer.java:28) 
 at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 
 at java.lang.Thread.run(Thread.java:748)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

梳理总结线程池知识(内含常用线程池选型技巧及其API分类讲解) 的相关文章

  • 使用现有同级属性值对属性进行 Jackson 多态反序列化

    我有一个现有的Request Response协议使用JSON我无法控制 示例1 响应JSON不需要任何多态反序列化 name simple response params success true 示例2 响应JSON需要对 params
  • 如何测试两个 Joda-Time DateTime 对象几乎相等?

    在单元测试中 我经常使用返回DateTime于或关于now 有没有办法说actual日期时间在几秒之内actual约会时间 这听起来是个坏主意 单元测试不应该以任何方式依赖于当前的实际时间 这就是为什么注入一些接口是一个很好的做法 称为Cl
  • 您最好的 Swing 设计模式和技巧是什么? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何使用 selenium 和 junit 测试多个浏览器(版本)

    我刚刚发现了硒 一个很棒的工具 我计划运行 使用 selenium ide 生成的 junit4 代码 但我需要它与许多浏览器 网络驱动程序一起运行 这个用例有 junit java 模式吗 我的第一个想法是使用 RunWith Param
  • 通过单击 imageView 打开活动

    我正在尝试做一个ImageView打开另一个活动 public class MainActivity extends Activity Override protected void onCreate Bundle savedInstanc
  • Java 中的双精度小数格式

    我在格式化双精度数的小数时遇到一些问题 如果我有双重值 例如4 0 如何格式化小数 使其变为 4 00 其中一种方法是使用数字格式 http docs oracle com javase tutorial i18n format decim
  • W/Firestore:[CustomClassMapper]:Android 类没有设置器/字段

    我试图使用 Recyclerview 从 Documents 类加载数据 但 logcat 上出现错误 W Firestore 21 1 1 CustomClassMapper No setter field for Document Na
  • Spring:如何将 KeyHolder 与 PostgreSQL 一起使用

    最近迁移到 POSTGRESQL 我试图获取在数据库表中创建新条目时唯一生成的密钥 桌子screenstable看起来像这样 CREATE TABLE screenstable id serial NOT NULL screenshot b
  • Spring Hibernate 4 支持

    我正在使用 Hibernate 4 CR1 我的应用程序之前使用 Spring hibernate 支持 版本 3 我还没有找到任何相关信息 是否有任何迹象表明 Spring 何时 或哪个版本 将提供对 Hibernate 4 的支持 UP
  • 如何将 csv/文本文件从 Android 手机发送到 WiFi 打印机?

    我正在开发一个 Android 应用程序 我应该从数据库中获取 csv txt 文件格式的数据 然后我必须将文件发送到 wifi 打印机 有谁知道我如何开始这样做 答案终于很简单 Socket client new Socket IP PO
  • 从文件夹中读取java文件

    我开发了一个应用程序 可以从用户选择的文件夹中读取文件 它显示每个文件中有多少行代码 我只想在文件选择器中显示 Java 文件 具有 java 扩展名的文件 下面是我的代码 public static void main String ar
  • 我的递归条件是否正确计算二叉树高度?

    我想在你的帮助下知道我的代码是对还是错 因为遗憾的是我无法运行它来检查 没有编译错误 我想做的是找到二叉树的高度 当然 树不必是平衡的 二叉树中的每个节点可以有两个节点作为子节点 http en wikipedia org wiki Bin
  • Java:如何实现通用二叉搜索树?

    到目前为止 我一直在编写一个 Node 类 class Node private value private Node left private Node right public int getValue return value pub
  • 获取 javax.crypto.IllegalBlockSizeException:使用填充密码解密时输入长度必须是 16 的倍数?

    使用 tomcat 我有两个 Web 应用程序 即 app1 和 app2 我以加密形式 使用下面的代码 将 url 从 app1 发送到 app2 然后在app2 我解密了这个加密的网址 但我在第 50 行低于异常decryp方法 Get
  • 纹理映射和光照顶点着色器错误 Java OpenGL

    我正在尝试将纹理映射到 3D 立方体并尝试编写着色器以使其具有照明和纹理 我尝试过只编写纹理着色器并且它有效 我还尝试过仅将 3D 值设置为红色的照明着色器 并且该照明着色器也可以工作 但当我尝试将两者结合起来时 我遇到了问题 我在下面提供
  • 从 java 反射中隐藏我的安全密钥

    下面的类是我用于加密的安全密钥提供程序 public class MySecretKey private String key 2sfdsdf7787fgrtdfg cj5 Some Util methods goes on Here 首先
  • 从 blob 反序列化 java 对象

    首先 我很抱歉 我要问一些愚蠢的问题 我根本不懂java 也不知道我们是否可以问这样的问题 如果没有 删除我的主题 oracle中有一个存储blob的表 它是二进制的 我能够解码它 输出看起来像这样 sr com epam insure c
  • 使用java连接到VPN后面的http服务器

    我想通过 VPN 连接到 REST Web 服务 Java 有没有办法在不使用操作系统功能的情况下建立到 VPN 网关的 pptp l2tp ipsec 连接和 HTTP 请求隧道 这很重要 因为我将从 servlet 连接到多个休息服务
  • 客户端和服务器之间的安全连接

    我正在开发一个服务器组件 它将为嵌入式客户端的请求提供服务 这也在我的控制之下 现在一切都是测试版 安全性是这样的 客户端通过 https 发送用户名 密码 服务器返回访问令牌 客户端使用自定义标头中的访问令牌通过 http 发出进一步请求
  • 为什么 java.io.File 没有 close 方法?

    While java io RandomAccessFile确实有一个close method java io File没有 这是为什么 文件在完成时会自动关闭吗 javadoc 的Fileclass 将该类描述为 文件和目录路径名的抽象表

随机推荐

  • Hive 基础知识

    目录 1 基础概念 1 1 定义 1 2 组件 1 3 元数据 1 4 内部表和外部表 2 Hive与关系型数据库的对比 3 Hive 数据存储 4 参考文献 1 基础概念 1 1 定义 Hive是一个基于Hadoop的数据仓库基础设施工具
  • notepad++安装十六进制插件备注

    notepad 安装十六进制插件备注 notepad 下载地址 https notepad plus plus org notepad 16进制插件下载位置 https github com chcg NPP HexEdit release
  • QKL123

    作者 QKL123 QKL123区块链排行榜包括区块链项目 区块链交易平台 区块链媒体 区块链公众号 区块链矿机 区块链矿池 EOS Dapp ETH Dapp 区块链钱包九大榜单 相对第二期 2019年02月 榜单 该期首次新增ETH D
  • java JDWP调试接口任意命令执行漏洞

    点击 仙网攻城狮 关注我们哦 不当想研发的渗透人不是好运维 让我们每天进步一点点 简介 JDWP Java DEbugger Wire Protocol 即Java调试线协议 是一个为Java调试而设计的通讯交互协议 它定义了调试器和被调试
  • 【数据结构】一、顺序表的基本操作(C语言)

    顺序表的定义 初始化 创建 打印 按值查找 插入 时间复杂度为O n 删除 时间复杂度为O n 销毁 合并两个顺序表 include
  • AI 绘画Stable Diffusion 研究(一)sd整合包v4.2 版本安装说明

    部署包作者 秋葉aaaki 免责声明 本安装包及启动器免费提供 无任何盈利目的 大家好 我是风雨无阻 众所周知 StableDiffusion 是非常强大的AI绘图工具 需要详细了解StableDiffusion的朋友 可查看我之前的这篇文
  • IntelliJ IDEA插件的Jrebel激活踩坑【内网离线使用】

    前言 Jrebel默认是需要外网在每一次使用时实时激活的 但是我们好多情况都是内网使用 所以我查了很多资料找到了一个解决方案 Jrebel 破解方法 https www jb51 net article 199354 htm Jrebel
  • python滚动的后推任意时点

    有几个关键点 对时间的循环 可以使用pandas的date range函数 比较容易一些 后推任意时点 使用dateutil relativedelta的relativedelta 可以以准确地指定日期后推 示例代码 比如 循环2020 0
  • react+antd出现preventDefault()警告报错

    react项目遇到如下报错 This synthetic event is reused for performance reasons If you re seeing this you re accessing the method p
  • 【C++】类和对象-多态

    1 多态的基本语法 代码 include
  • 【微信小程序】小程序长按复制文本

    微信小程序的文本 要具有长按复制功能 必须满足两个条件 文本在
  • Python类、模块、包

    http www cppblog com len archive 2008 07 24 57078 html Python在处理功能复用和功能颗粒度划分时采用了类 模块 包的结构 这种处理跟C 中的类和名字空间类似 但更接近于Java所采用
  • linux开发板通过网线连接电脑(win10)连接网络问题

    最近开始学习嵌入式Linux开发 使用野火imx6ull开发板 想把开发板通过网线连接到笔记本 笔记本连接WiFi 共享使用网络 查询了很多资料后成功实现 我现在把这个方法分享出来 1 禁用防火墙功能 打开网络和共享中心 gt window
  • .net dapper简单使用

    以本地mysql数据库为例 准备工作 新建数据库 新建表 例如book表 然后定义Book类 book表和Book类应对应 这样dapper才能把他们映射好 在appsettings json中配置数据库连接 ConnectionStrin
  • pytorch autograd计算标量函数二阶导数

    计算标量函数 y x 3 s i n
  • 微信公众号实现简易的物联网控制(一)

    这篇主要说说如何通过微信公众号来查看室内传感器数据 至于硬件部分和物联网平台以后再详细说明 准备工作 1 申请微信公众号 2 搭建云服务器 首先说明一下整体流程 用户发送相应的指令到公众号后台 服务器根据指令的内容调用OneNET的API获
  • 深入理解JVM(四)JVM的垃圾回收机制

    文章目录 1 什么是垃圾回收机制 2 Java中的引用类型 3 如何判断对象是否可以被回收 4 方法区的垃圾收集 5 垃圾收集算法 5 1 标记 清除 Mark Sweep 算法 5 2 标记整理 Mark Compact 算法 5 3 复
  • 谭浩强C++课后习题16——矩阵对角线元素之和

    谭浩强C 课后习题16 矩阵对角线元素之和 题目描述 求一个n n矩阵对角线元素之和 算法思路 定义一个动态二维数组 定义方法 定义一个指向指针的指针 令其指向每一行的首地址 循环n次 定义n个一维数组 循环n次 对角线之和即为每一行num
  • Dynamics 365发送邮件

    在Dynamics 365 CRM 开发中 发送邮件除了CRM自带的邮件以外 还可以使用代码发送邮件 大大丰富了邮件的内容 满足客户不同的需求 Entity email new Entity email 邮件接收人 类型为 EntityCo
  • 梳理总结线程池知识(内含常用线程池选型技巧及其API分类讲解)

    核心 救急线程概念 核心线程 救急线程 二者区别 原始线程池的构造方法 常用线程池及其适用场景 FixedThreadPool CachedThreadPool SingleThreadExecutor ScheduledExecutor