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

news2025/2/9 1:56:14

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 来显式配置线程池参数。
  • 合理配置线程池参数,并根据任务类型优化,是高效并发编程的关键。

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

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

相关文章

代码随想录算法【Day38】

Day38 322. 零钱兑换 思路 完全背包 代码 class Solution { public:int coinChange(vector<int>& coins, int amount) {vector<int> dp(amount 1, INT_MAX);dp[0] 0;for (int i 0; i < coins.size(); i) { // 遍历物品for (int j coins[i]; j <…

51单片机之冯·诺依曼结构

一、概述 8051系列单片机将作为控制应用最基本的内容集成在一个硅片上&#xff0c;其内部结构如图4-1所示。作为单一芯片的计算机&#xff0c;它的内部结构与一台计算机的主机非常相似。其中微处理器相当于计算机中的CPU&#xff0c;由运算器和控制器两个部分构成&#xff1b;…

02.07 TCP服务器与客户端的搭建

一.思维导图 二.使用动态协议包实现服务器与客户端 1. 协议包的结构定义 首先&#xff0c;是协议包的结构定义。在两段代码中&#xff0c;pack_t结构体都被用来表示协议包&#xff1a; typedef struct Pack {int size; // 记录整个协议包的实际大小enum Type type; …

【CubeMX+STM32】SD卡 文件系统读写 FatFs+SDIO+DMA

本篇&#xff0c;将使用CubeMXKeil&#xff0c;创建一个SD卡的 FatFSSDIODMA 文件系统读写工程。 目录 一、简述 二、CubeMX 配置 FatFSSDIO DMA 三、Keil 编辑代码 四、实验效果 实现效果&#xff0c;如下图&#xff1a; 一、简述 上两篇&#xff0c;已循序渐进讲解了SD、…

51单片机之使用Keil uVision5创建工程以及使用stc-isp进行程序烧录步骤

一、Keil uVision5创建工程步骤 1.点击项目&#xff0c;新建 2.新建目录 3.选择目标机器&#xff0c;直接搜索at89c52选择&#xff0c;然后点击OK 4.是否添加起吊文件&#xff0c;一般选择否 5.再新建的项目工程中添加文件 6.选择C文件 7.在C文件中右键&#xff0c;添加…

aws(学习笔记第二十七课) 使用aws API Gateway+lambda体验REST API

aws(学习笔记第二十七课) 使用aws API Gatewaylambda体验REST API 学习内容&#xff1a; 使用aws API Gatewaylambda 1. 使用aws API Gatewaylambda 作成概要 使用api gateway定义REST API&#xff0c;之后再接收到了http request之后&#xff0c;redirect到lambda进行执行。…

5 前端系统开发:Vue2、Vue3框架(上):Vue入门式开发和Ajax技术

文章目录 前言一、Vue框架&#xff08;简化DOM操作的一个前端框架&#xff09;&#xff1a;基础入门1 Vue基本概念2 快速入门&#xff1a;创建Vue实例&#xff0c;初始化渲染&#xff08;1&#xff09;创建一个入门Vue实例&#xff08;2&#xff09;插值表达式&#xff1a;{{表…

快速在wsl上部署学习使用c++轻量化服务器-学习笔记

知乎上推荐的Tinywebserver这个服务器&#xff0c;快速部署搭建&#xff0c;学习c服务器开发 仓库地址 githubhttps://link.zhihu.com/?targethttps%3A//github.com/qinguoyi/TinyWebServerhttps://link.zhihu.com/?targethttps%3A//github.com/qinguoyi/TinyWebServer 在…

2025年Android NDK超全版本下载地址

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

React 设计模式:实用指南

React 提供了众多出色的特性以及丰富的设计模式&#xff0c;用于简化开发流程。开发者能够借助 React 组件设计模式&#xff0c;降低开发时间以及编码的工作量。此外&#xff0c;这些模式让 React 开发者能够构建出成果更显著、性能更优越的各类应用程序。 本文将会为您介绍五…

B站自研的第二代视频连麦系统(上)

导读 本系列文章将从客户端、服务器以及音视频编码优化三个层面&#xff0c;介绍如何基于WebRTC构建视频连麦系统。希望通过这一系列的讲解&#xff0c;帮助开发者更全面地了解 WebRTC 的核心技术与实践应用。 背景 在文章《B站在实时音视频技术领域的探索与实践》中&#xff…

使用Python实现PDF与SVG相互转换

目录 使用工具 使用Python将SVG转换为PDF 使用Python将SVG添加到现有PDF中 使用Python将PDF转换为SVG 使用Python将PDF的特定页面转换为SVG SVG&#xff08;可缩放矢量图形&#xff09;和PDF&#xff08;便携式文档格式&#xff09;是两种常见且广泛使用的文件格式。SVG是…

[渗透测试]热门搜索引擎推荐— — shodan篇

[渗透测试]热门搜索引擎推荐— — shodan篇 免责声明&#xff1a;本文仅用于分享渗透测试工具&#xff0c;大家使用时&#xff0c;一定需要遵守相关法律法规。 除了shodan&#xff0c;还有很多其他热门的&#xff0c;比如&#xff1a;fofa、奇安信的鹰图、钟馗之眼等&#xff0…

基于物联网技术的智能寻车引导系统方案:工作原理、核心功能及系统架构

本文专为IT技术员、软件开发工程师及智能停车领域专业人士打造&#xff0c;旨在深入剖析智能寻车引导系统的构建与优化过程。如需获取详细解决方案可前往文章最下方获取&#xff0c;如有项目需求及技术合作可私信作者。 智能寻车引导系统是一种集智能化、自动化于一体的停车管理…

【React】合成事件语法

React 合成事件是 React 为了处理浏览器之间的事件差异而提供的一种跨浏览器的事件系统。它封装了原生的 DOM 事件&#xff0c;提供了一致的事件处理机制。 合成事件与原生事件的区别&#xff1a; 合成事件是 React 自己实现的&#xff0c;封装了原生事件。合成事件依然可以通…

Redis02 - 持久化

Redis持久化 文章目录 Redis持久化一&#xff1a;持久化简介1&#xff1a;Redis为什么要进行持久化2&#xff1a;Redis持久化的方式 二&#xff1a;RDB持久化介绍1&#xff1a;手动触发RDB2&#xff1a;自动触发RDB3&#xff1a;redis.conf中进行RDB的配置4&#xff1a;RDB优缺…

初始JavaEE篇 —— Spring Web MVC入门(上)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 RequestMappingg 注解介绍 Postman的介绍与使用 PostMapping 与 GetMapping 注解 构造并接收请求 接收简单参数 接收对象…

Leetcode—487. 最大连续1的个数 II【中等】Plus

2025每日刷题&#xff08;210&#xff09; Leetcode—487. 最大连续1的个数 II 实现代码 class Solution { public:int findMaxConsecutiveOnes(vector<int>& nums) {int zeros 0;int ans 0;for(int l 0, r 0; r < nums.size(); r) {if(nums[r] 0) {zeros;…

【MySQL】窗口函数详解(概念+练习+实战)

文章目录 前言1. SQL窗口函数 1.1 窗口函数概念1.2 窗口函数语法1.3 常见窗口函数 1.3.1 聚合窗口函数1.3.2 专用窗口函数 1.4 窗口函数性能比较 2. LeetCode 例题 2.1 LeetCode SQL 178&#xff1a;分数排名2.2 LeetCode SQL 184&#xff1a;最高工资2.3 LeetCode SQL 185&am…

前端组件标准化专家Prompt指令的最佳实践

前端组件标准化专家Prompt 提示词可作为项目自定义提示词使用&#xff0c;本次提示词偏向前端开发的使用&#xff0c;如有需要可适当修改关键词和示例 推荐使用 Cursor 中作为自定义指令使用Cline 插件中作为自定义指令使用在力所能及的范围内使用最好的模型&#xff0c;可以…