Java 线程池:7参数配置、4拒绝策略与执行流程详解

1. 为什么需要线程池?

在 Java 并发编程中,线程的创建和销毁是一项昂贵的操作。频繁地创建和销毁线程会带来较高的系统开销,甚至可能因线程数过多而导致 OOM(OutOfMemoryError)CPU 过载
线程池(Thread Pool) 的设计初衷正是为了解决这些问题。

线程池的主要优点:

  • 降低资源消耗:通过复用线程,避免频繁的创建和销毁。
  • 提高响应速度:任务到达时可复用现有线程,无需等待线程创建。
  • 增强可管理性:提供任务队列和线程管理能力,防止资源耗尽。

2. 线程池的核心组成

Java 提供了 ThreadPoolExecutor 作为线程池的核心实现,构造方法包含多个参数,主要负责线程池的各种行为控制:

2.1. 构造方法(7 个参数)

public ThreadPoolExecutor(
    int corePoolSize,         // 核心线程数
    int maximumPoolSize,      // 最大线程数
    long keepAliveTime,       // 空闲线程存活时间
    TimeUnit unit,            // 时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列
    ThreadFactory threadFactory,       // 线程工厂
    RejectedExecutionHandler handler   // 拒绝策略
)

参数详解:

参数名说明
corePoolSize核心线程数,线程池始终保持的最小线程数量。
maximumPoolSize最大线程数,线程池可创建的最大线程数。
keepAliveTime非核心线程空闲超过该时间会被销毁。
unitkeepAliveTime 的时间单位。
workQueue用于存放等待执行的任务。
threadFactory创建新线程的工厂,可自定义线程属性。
handler当线程池无法接收新任务时的拒绝策略。

示例:创建线程池

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 4, 60, TimeUnit.SECONDS, 
    new LinkedBlockingQueue<>(10),
    Executors.defaultThreadFactory(), 
    new ThreadPoolExecutor.AbortPolicy()
);
  • 核心线程数为 2,最大线程数为 4
  • 多余线程在空闲 60 秒后销毁
  • 任务队列最多容纳 10 个任务
  • 使用默认线程工厂创建线程
  • 拒绝策略为 AbortPolicy,即直接抛出异常

3. 创建线程池的方式

3.1. 使用 Executors 创建线程池

Java 提供了 Executors 工具类,可以快速创建不同类型的线程池:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
线程池类型创建方法特点
固定大小线程池newFixedThreadPool(n)线程数固定,适合长期任务。
缓存线程池newCachedThreadPool()线程数不固定,适合短期任务。
单线程池newSingleThreadExecutor()只有一个线程,任务顺序执行。
调度线程池newScheduledThreadPool(n)支持定时和周期性任务。

3.2. 推荐使用 ThreadPoolExecutor 创建线程池

尽管 Executors 提供了便捷的方法,但其内部参数可能存在潜在风险,例如 无界队列可能导致 OOM。因此,推荐显式使用 ThreadPoolExecutor 并自定义参数:

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
    2, 4, 60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(2),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);

4. 线程池的任务执行流程

线程池任务执行过程可以分为以下几个阶段:

任务提交
线程数 < corePoolSize ?
创建新线程执行任务
任务队列已满 ?
任务加入队列
线程数 < maximumPoolSize ?
创建新线程
执行拒绝策略
线程执行完成
线程是否空闲超时 ?
线程回收

4.1. 任务执行流程说明

  1. 线程数 < 核心线程数(corePoolSize):创建新线程执行任务。
  2. 线程数达到核心线程数,任务进入任务队列,等待执行。
  3. 任务队列已满,且线程数小于最大线程数(maximumPoolSize):创建新线程执行任务。
  4. 线程数达到 maximumPoolSize 且任务队列已满:执行拒绝策略。

4.2. 线程池中的两阶段回收机制

线程池中的线程回收遵循两阶段机制:

  1. 核心线程(corePoolSize 内的线程)
    核心线程默认情况下是长期存活的,除非显式调用 allowCoreThreadTimeOut(true),才会在空闲超过 keepAliveTime 后被回收。
executor.allowCoreThreadTimeOut(true);
  1. 非核心线程(超过 corePoolSize 的线程)
    非核心线程会在空闲超过 keepAliveTime 后自动销毁,防止资源浪费。

5. 线程池的拒绝策略

当任务无法被线程池接受时,ThreadPoolExecutor 提供了 4 种内置拒绝策略:

拒绝策略描述
AbortPolicy抛出 RejectedExecutionException(默认策略)。
CallerRunsPolicy由提交任务的线程执行该任务,减轻线程池压力。
DiscardPolicy丢弃该任务,不抛出异常。
DiscardOldestPolicy丢弃队列中最早的任务,尝试执行当前任务。

示例:

new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(2),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.CallerRunsPolicy());

6. 常见问题与优化建议

  1. 避免使用 Executors 创建线程池,推荐显式参数化 ThreadPoolExecutor
  2. 合理配置 corePoolSizemaximumPoolSize
    • CPU 密集型任务(如计算):corePoolSize = CPU 核数 + 1
    • I/O 密集型任务(如数据库操作):corePoolSize = CPU 核数 * 2
  3. 选择合适的任务队列
    • LinkedBlockingQueue:适用于任务较多时,防止任务丢失。
    • SynchronousQueue:适用于高吞吐、低延迟场景。
  4. 监控线程池状态
System.out.println("Active threads: " + threadPool.getActiveCount());
System.out.println("Task queue size: " + threadPool.getQueue().size());
  1. 避免线程池泄漏:执行完任务后,调用 shutdown()shutdownNow() 释放资源。

7. 总结

  • 线程池能有效提升并发性能,减少系统资源开销。
  • Executors 提供便捷的方法,但推荐使用 ThreadPoolExecutor 来显式配置线程池参数。
  • 合理配置线程池参数,并根据任务类型优化,是高效并发编程的关键。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis --- 使用HyperLogLog实现UV(访客量)

UV 和 PV 是网站或应用数据分析中的常用指标&#xff0c;用于衡量用户活跃度和页面访问量。 UV (Unique Visitor 独立访客)&#xff1a; 指的是在一定时间内访问过网站或应用的独立用户数量。通常根据用户的 IP 地址、Cookies 或用户 ID 等来唯一标识一个用户。示例&#xff1…

【机器学习案列】糖尿病风险可视化及预测

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

单片机之基本元器件的工作原理

一、二极管 二极管的工作原理 二极管是一种由P型半导体和N型半导体结合形成的PN结器件&#xff0c;具有单向导电性。 1. PN结形成 P型半导体&#xff1a;掺入三价元素&#xff0c;形成空穴作为多数载流子。N型半导体&#xff1a;掺入五价元素&#xff0c;形成自由电子作为多…

llama.cpp GGUF 模型格式

llama.cpp GGUF 模型格式 1. Specification1.1. GGUF Naming Convention (命名规则)1.1.1. Validating Above Naming Convention 1.2. File Structure 2. Standardized key-value pairs2.1. General2.1.1. Required2.1.2. General metadata2.1.3. Source metadata 2.2. LLM2.2.…

Conmi的正确答案——Rider中添加icon作为exe的图标

C#版本&#xff1a;.net 8.0 Rider版本&#xff1a;#RD-243.22562.250&#xff08;非商业使用版&#xff09; 1、添加图标到解决方案下&#xff1a; 2、打开“App.xaml”配置文件&#xff0c;添加配置&#xff1a; <Applicationx:Class"ComTransmit.App"xmlns&q…

告别手动操作!用Ansible user模块高效管理 Linux账户

在企业运维环境中&#xff0c;服务器的用户管理是一项基础但非常重要的任务。比如&#xff0c;当有新员工加入时&#xff0c;我们需要在多台服务器上为他们创建账户并分配合适的权限。而当员工离职或岗位发生变化时&#xff0c;我们也需要迅速禁用或删除他们的账户&#xff0c;…

C++小等于的所有奇数和=最大奇数除2加1的平方。

缘由 三种思路解题&#xff1a;依据算术推导得到一个规律&#xff1a;小等于的所有奇数和等于最大奇数除以2加1的平方。将在后续发布&#xff0c;总计有十种推导出来的实现代码。 int a 0,aa 1,aaa 0;cin >> a; while (aa<a) aaa aa, aa 2;cout << aaa;i…

【CPP】CPP经典面试题

文章目录 引言1. C 基础1.1 C 中的 const 关键字1.2 C 中的 static 关键字 2. 内存管理2.1 C 中的 new 和 delete2.2 内存泄漏 3. 面向对象编程3.1 继承和多态3.2 多重继承 4. 模板和泛型编程4.1 函数模板4.2 类模板 5. STL 和标准库5.1 容器5.2 迭代器 6. 高级特性6.1 移动语义…

深入浅出谈VR(虚拟现实、VR镜头)

1、VR是什么鬼&#xff1f; 近两年VR这次词火遍网上网下&#xff0c;到底什么是VR&#xff1f;VR是“Virtual Reality”&#xff0c;中文名字是虚拟现实&#xff0c;是指采用计算机技术为核心的现代高科技手段生成一种虚拟环境&#xff0c;用户借助特殊的输入/输出设备&#x…

【Redis】安装配置Redis超详细教程 / Linux版

Linux安装配置Redis超详细教程 安装redis依赖安装redis启动redis停止redisredis.conf常见配置设置redis为后台启动修改redis监听地址设置工作目录修改密码监听的端口号数据库数量设置redis最大内存设置日志文件设置redis开机自动启动 学习视频&#xff1a;黑马程序员Redis入门到…

[LeetCode]day16 242.有效的字母异位词

242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的 字母异位词 示例 1: 输入: s "anagram", t "nagaram" 输出: true示例 2: 输入: s "rat"…

[MoeCTF 2022]baby_file

题目 <html> <title>Heres a secret. Can you find it?</title> <?phpif(isset($_GET[file])){$file $_GET[file];include($file); }else{highlight_file(__FILE__); } ?> </html> 读取flag /?filephp://filter/readconvert.base64-encode…

Centos挂载镜像制作本地yum源,并补装图形界面

内网环境centos7.9安装图形页面内网环境制作本地yum源 上传镜像到服务器目录 创建目录并挂载镜像 #创建目录 cd /mnt/ mkdir iso#挂载 mount -o loop ./CentOS-7-x86_64-DVD-2009.iso ./iso #前面镜像所在目录&#xff0c;后面所挂载得目录#检查 [rootlocalhost mnt]# df -h…

判断您的Mac当前使用的是Zsh还是Bash:echo $SHELL、echo $0

要判断您的Mac当前使用的是Zsh还是Bash&#xff0c;可以使用以下方法&#xff1a; 查看默认Shell: 打开“终端”应用程序&#xff0c;然后输入以下命令&#xff1a; echo $SHELL这将显示当前默认使用的Shell。例如&#xff0c;如果输出是/bin/zsh&#xff0c;则说明您使用的是Z…

python 小游戏:扫雷

目录 1. 前言 2. 准备工作 3. 生成雷区 4. 鼠标点击扫雷 5. 胜利 or 失败 6. 游戏效果展示 7. 完整代码 1. 前言 本文使用 Pygame 实现的简化版扫雷游戏。 如上图所示&#xff0c;游戏包括基本的扫雷功能&#xff1a;生成雷区、左键点击扫雷、右键标记地雷、显示数字提示…

【重新认识C语言----文件管理篇】

目录 ​编辑 -----------------------------------------begin------------------------------------- 引言 1. 文件的基本概念 2. 文件指针 3. 文件的打开与关闭 3.1 打开文件 3.2 关闭文件 4. 文件的读写操作 4.1 读取文件 4.1.1 使用fgetc()读取文件 4.1.2 使用fg…

EasyExcel 导出合并层级单元格

EasyExcel 导出合并层级单元格 一、案例 案例一 1.相同订单号单元格进行合并 合并结果 案例二 1.相同订单号的单元格进行合并2.相同订单号的总数和总金额进行合并 合并结果 案例三 1.相同订单号的单元格进行合并2.相同订单号的商品分类进行合并3.相同订单号的总数和总金额…

WPF 进度条(ProgressBar)示例一

本文讲述&#xff1a;WPF 进度条(ProgressBar)简单的样式修改和使用。 进度显示界面&#xff1a;使用UserControl把ProgressBar和进度值以及要显示的内容全部组装在UserControl界面中&#xff0c;方便其他界面直接进行使用。 <UserControl x:Class"DefProcessBarDemo…

LabVIEW自定义测量参数怎么设置?

以下通过一个温度采集案例&#xff0c;说明在 LabVIEW 中设置自定义测量参数的具体方法&#xff1a; 案例背景 ​ 假设使用 NI USB-6009 数据采集卡 和 热电偶传感器 监测温度&#xff0c;需自定义以下参数&#xff1a; 采样率&#xff1a;1 kHz 输入量程&#xff1a;0~10 V&a…

新能源产业的质量革命:六西格玛培训如何重塑制造竞争力

在新能源行业狂飙突进的今天&#xff0c;企业若想在全球供应链中占据高地&#xff0c;仅靠技术突破已远远不够。制造效率的毫厘之差&#xff0c;可能成为市场话语权的千里之距。某光伏巨头曾因电池片良率低于行业均值1.5%&#xff0c;导致年损失超2.3亿元——这恰恰印证了六西格…