【Java】线程池技术(二)ThreadPoolExecutor的基本定义

news2024/11/29 8:42:54

线程池初始化与定义

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, 
                          long keepAliveTime, TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

线程池构造方法的入参含义分别如下:

  • corePoolSize:核心线程数,必须大于或等于 0
  • maximumPoolSize:最大线程数,必须大于 0 且大于或等于核心线程数
  • keepAliveTime:空闲线程存活时间,必须大于或等于 0
  • unit:存活时间单位 TimeUnit
  • workQueue:阻塞队列,存储通过 execute() 方法提交的未能开始执行的任务。常见的选择有:
    • ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO
    • PriorityBlockingQueue:具有优先级的无界阻塞队列,较少使用
    • LinkedBlockingQueue:基于链表结构的无界阻塞队列,吞吐量通常要高于 ArrayBlockingQueue,FIFO
    • SynchronousQueue:不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQueue
  • threadFactory:线程创建工厂,一般可以用默认的
  • handler:饱和/拒绝策略,有以下四种策略:
    • ThreadPoolExecutor.AbortPolicy:(默认)阻塞队列已满,丢弃任务并抛出 RejectedExecutionException 异常。
    • ThreadPoolExecutor.DiscardPolicy:阻塞队列已满,丢弃任务,但是不抛出异常。
    • ThreadPoolExecutor.DiscardOldestPolicy:阻塞队列已满,丢弃队列最前面的任务,然后重新尝试加入队列执行任务(重复此过程)。
    • ThreadPoolExecutor.CallerRunsPolicy:由发起调用的线程自己去执行该任务(如果主线程运行结束,则丢弃该任务);会降低新任务的提交速度,影响程序的整体性能。
    • 也可以根据实际需求自定义拒绝策略,实现 RejectedExecutionHandler 接口

当 ThreadPoolExecutor 线程池被创建的时候,里面是没有创建工作线程的,直至有任务调用了 execute() 方法时,才开始创建工作线程。除非调用 prestartAllCoreThreads() 或者 prestartCoreThread() 方法,可以手动预创建线程。调用 execute() 方法时的具体工作原理为:

  1. 如果当前工作线程数小于核心线程数,则创建新的线程执行任务,否则将任务加入阻塞队列;
  2. 如果阻塞队列满了,则根据最大线程数创建额外(非核心工作线程)的工作线程去执行任务;
  3. 如果工作线程数达到了线程池允许的最大线程数,则根据拒绝策略去执行。
  4. 非核心线程的存活时间到期的话,线程资源将会被回收。

核心线程在线程池刚创建的时候还未被创建,随着任务的执行才会创建新的核心线程。线程池启动使用后,核心线程默认不会被回收,除非通过方法 allowCoreThreadTimeOut(boolean value) 启用了空闲核心线程回收,此时 keepAliveTime 才会对核心线程也生效。调用该方法启动空闲核心线程回收时,会马上执行一次回收的操作。

除了默认的线程池 ThreadPoolExecutor() 以外,Java 还通过 Executors 定义了四种线程池:

// 1. CachedThreadPool 有缓冲的线程池,具体线程数由JVM控制,灵活创建于回收线程资源,适用于并发执行大量短期的小任务
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}

// 2. FixedThreadPool 固定大小的线程池(所有线程都是核心线程),适用于处理CPU密集型的任务
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}

// 3. ScheduledThreadPool 定时执行任务的线程池,内部使用延时队列存储阻塞任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}

// 4. SingleThreadExecutor 单线程的线程池,只有一个线程在工作,适用于串行执行任务的场景
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService(
            new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
        );
}

其中,newScheduledThreadPool 的具体实现为:

// ScheduledThreadPoolExecutor.java
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
    
// ThreadPoolExecutor.java
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);
}

本质上,以上的各类线程池还是通过 ThreadPoolExecutor 类实现的初始化。但是,在阿里巴巴 Java 开发手册中明确指出,不允许使用 Executors 创建线程池:

img

从上面 FixedThreadPool 和 SingleThreadPool 的构造函数可以得知,初始化 LinkedBlockingQueue 时没有指定其容量。LinkedBlockingQueue 是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,默认最大长度为 Integer.MAX_VALUE。对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。

CachedThreadPool 和 ScheduledThreadPool 这两个线程池虽然没有这种无边界的阻塞队列的情况,但这两种线程池可达到的最大线程数可能是 Integer.MAX_VALUE ,而创建这么多线程,就有非常大的概率导致 OOM。

为了避免出现以上问题,一个直接的思路就是避免使用其默认的构造实现。我们可以根据实际应用场景,通过自定义构造参数,直接调用 ThreadPoolExecutor 的构造方法来创建线程池。初始化阻塞队列时,明确指定队列大小即可。

当实际提交的线程数超过了当前允许的最大线程数时,就会抛出 java.util.concurrent.RejectedExecutionException ,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是抛出异常(Exception)进行捕获总比发生错误(Error)影响程序运行要好。

同时,手动创建线程池还可以指定线程名称,这样便于对线程池的运行情况进行监控和追踪。

综上所属,生产环境中常用的线程池构造方法如下:

  1. 通过 ThreadPoolExecutor 构造函数实现
  2. 通过 Executors 工具类来创建不同类型的 ThreadPoolExecutor 线程池。

其中,更多的推荐使用第一种方法。

线程池参数的配置

线程池线程数大小是一个值得仔细斟酌设置的参数。

如果设置过小,当同一时间出现大量任务需要执行时,可能会导致大量任务在阻塞队列中排队等待,甚至会出现队列满员后任务无法处理的情况,或大量任务堆积导致的 OOM。这种场景下 CPU 资源没有得到充分的利用。

如果设置过大,大量线程可能会同时竞争 CPU 资源,导致大量的上下文切换,从而增加线程的执行时间,影响了整体的执行效率。

线程池参数的选用,大部分都是需要根据实际测试结果去调整得出最佳配置。不过有一些简单的经验值可以参考一下:

  • IO 密集型任务:2N。系统大部分时间都用来处理 IO 交互,期间不会占用 CPU 资源,此时释放出来的 CPU 资源可以用于其他线程执行任务,因此可以尽可能多的配置线程。
  • CPU 密集型任务:N+1。任务中存在大量复杂的运算,消耗的主要是 CPU 资源。多出来的一个线程是为了防止线程偶发的缺页中断,或者其他原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,此时多出来的一个线程就可以将 CPU 空闲时间利用起来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1844437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CRASH】freelist异常导致的异常地址访问

freelist异常导致的异常地址访问 问题现象初步分析继续深入新的发现沙盘推演寻找元凶分析代码后记 问题现象 项目一台设备几天内出现了两次crash&#xff0c;都是异常地址访问导致。 [66005.261660] BUG: unable to handle page fault for address: ffffff8881575110初步分析…

【星环社区版TDH2024年度大事件】全新版本?全新组件?性能提升10倍?

TDH社区版家族迎来新成员 不知不觉社区版已经陪伴大家将近两年的时间了&#xff0c;在这两年里收获到了很多认可&#xff0c;同时也收获到了一些建议与意见&#xff0c;比如资源成本的问题。在去年我们发布了TDH社区开发版&#xff0c;仅需单台服务器即可一键安装部署Inceptor…

链表OJ

GDUFE 在期末前再刷一次链表题 ~ 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* removeElements(struct ListNode* head, int …

互联网应用主流框架整合之Spring Boot基本概念

Spring Boot是用来简化Spring应用程序的搭建、开发、测试和部署过程的&#xff0c;该框架使用了特定的方式进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置&#xff0c;SpringBoot致力于快速应用开发(Rapid Application Development)领域的发展&#xff0c;它通过…

代码随想录训练营Day 63|力扣42. 接雨水、84.柱状图中最大的矩形

1.接雨水 代码随想录 代码&#xff1a;(单调栈) class Solution { public:int trap(vector<int>& height) {int result 0;stack<int> st;st.push(0);for(int i 1; i < height.size(); i){if(height[i] < height[st.top()]){st.push(i);}else if(heigh…

六西格玛目标设定的时候需要考虑哪些因素?

在追求企业卓越绩效的道路上&#xff0c;六西格玛管理方法论以其严谨的数据驱动和持续改进的理念&#xff0c;成为众多企业的首选工具。然而&#xff0c;要想真正发挥六西格玛的潜力&#xff0c;合理而精准的目标设定至关重要。那么&#xff0c;六西格玛目标设定的时候需要考虑…

新火种AI|英伟达市值超越微软!AI技术如何重塑科技股价值?

作者&#xff1a;一号 编辑&#xff1a;美美 AI&#xff0c;正带着美股狂奔。 2024年&#xff0c;英伟达&#xff08;NVIDIA&#xff09;以其在人工智能&#xff08;AI&#xff09;领域的卓越表现&#xff0c;市值首次超越了科技巨头微软&#xff0c;成为全球市值最高的公司…

变压器电机绕组阻值测试

产品概述 武汉凯迪正大变压器直流电阻的测量是变压器制造中半成品、成品出厂试验、安装、交接试验及电力部门预防性试验项目&#xff0c;能有效发现变压器线圈的选材、焊接、连接部位松动、缺股、断线等制造缺陷和运行后存在的隐患。KDZRS-20A直流电阻测试仪是测量变压器、互感…

XGBOOST案例

最近我在Kaggle上找到一个跟XGBOOST相关的代码&#xff0c;这有助于我们去实战性的学习。 这段代码旨在使用XGBoost和TPU进行大规模的分子绑定预测。 比赛项目&#xff1a;NeurIPS 2024 - Predict New Medicines with BELKA | Kaggle 训练样本代码&#xff1a; 上图是我们已…

ElasticSearch学习笔记(二)文档操作、RestHighLevelClient的使用

文章目录 前言3 文档操作3.1 新增文档3.2 查询文档3.3 修改文档3.3.1 全量修改3.3.2 增量修改 3.4 删除文档 4 RestAPI4.1 创建数据库和表4.2 创建项目4.3 mapping映射分析4.4 初始化客户端4.5 创建索引库4.6 判断索引库是否存在4.7 删除索引库 5 RestClient操作文档5.1 准备工…

怎么把答案去掉打印?超详细步骤告诉你!

在数字化教育日益普及的今天&#xff0c;我们时常需要在电子试卷和纸质试卷之间进行转换。然而&#xff0c;许多时候我们并不需要答案部分&#xff0c;这就需要我们掌握一些工具来去除答案&#xff0c;以便打印出纯净的试卷。本文将为您详细介绍如何使用试卷星、拍试卷以及WPS …

Srouce Insight 4出现乱码

今天用SI4打开一个工程文件&#xff0c;一打开发现注释全是乱码。中文全部看不出来&#xff0c;英文和数字可以看得出来。 那是因为中文的编码格式不算特别兼容。所以需要调整编码格式。 于是我在这里调整了编码格式&#xff1a; 找到菜单的Options-Preferences里面的Files 调…

数据集MNIST手写体识别 pyqt5+Pytorch/TensorFlow

GitHub - LINHYYY/Real-time-handwritten-digit-recognition: VGG16和PyQt5的实时手写数字识别/Real-time handwritten digit recognition for VGG16 and PyQt5 pyqt5Pytorch内容已进行开源&#xff0c;链接如上&#xff0c;请遵守开源协议维护开源环境&#xff0c;如果觉得内…

Linux 系统图像化编程GTK入门

环境前期准备 演示环境&#xff1a;Windows 11 Ubuntu 22.04.4 VS Code 前提条件&#xff1a;1、Windows 11 子系统Ubuntu 22.04.4 已经安装图形化界面&#xff0c;如果没有安装请参考文章&#xff1a; windows11子系统Ubuntu 22.04.4子安装图形化界面 2、Ubuntu 22.04.4…

戏剧之家杂志戏剧之家杂志社戏剧之家编辑部2024年第14期目录

文艺评论 南戏瓯剧跨文化传播研究 陈晓东;高阳;许赛梦; 3-7 论互联网时代的戏剧传播与批评——以西法大剧社和南山剧社为例 邬慧敏; 8-10 “左手荒诞&#xff0c;右手温情”——《西西弗神话》在戏剧《第七天》中的接受探究 赵稳稳; 11-13 戏剧研讨《戏剧之家》投稿…

深入浅出Netty:高性能网络应用框架的原理与实践

深入浅出Netty&#xff1a;高性能网络应用框架的原理与实践 1. Netty简介 Netty是一个基于Java的异步事件驱动的网络应用框架&#xff0c;广泛用于构建高性能、高可扩展性的网络服务器和客户端。它提供对多种协议&#xff08;如TCP、UDP、SSL等&#xff09;的支持&#xff0c;…

[SAP ABAP] 数据类型

1.基本数据类型 示例1 默认定义的基本数据类型是CHAR数据类型 输出结果: 示例2 STRING数据类型用于存储任何长度可变的字符串 输出结果: 示例3 DATE数据类型用于存储日期信息&#xff0c;并且可以存储8位数字 输出结果: 提示Tips&#xff1a;日期和时间类型的变量可以直接进…

重量级身份证明来了!政府颁发的这一证书很给力

在近日的一项重要公告中&#xff0c;北京市科学技术委员会、北京市发展和改革委员会、北京市经济和信息化局等五大部门联合公示 2023 年度第二批&#xff08;总第十九批&#xff09;北京市新技术新产品新服务名单。涛思数据旗下高性能、分布式的物联网、工业大数据平台 TDengin…

CFA官网资料说明

进入到资料后台你就会发现&#xff0c;分了三个板块&#xff0c;分别是Study, Prepare和The Exam。 Study板块 主要提供备考重要资料&#xff0c;包括教材下载、自学习系统 Prepare板块 主要帮助考生准备考试&#xff0c;提供了一些小工具、包括机考软件指南 The exam板块…

whiteboard - 笔记

1 drawio draw.io GitHub - jgraph/drawio: draw.io is a JavaScript, client-side editor for general diagramming. 2 demo 可以将XML数据保存到服务器上的data目录。需要在服务器端创建一个接收和处理POST请求的脚本,该脚本将接收到的SVG数据保存到指定的文件中。下面是…