Java并发编程-线程池(二)

文章目录

  • 线程池的实现原理
    • execute(Runnable command)
      • **1. 阶段一:尝试创建核心线程**
      • **2. 阶段二:尝试将任务加入队列**
      • **3. 阶段三:尝试创建非核心线程或拒绝任务**
      • **关键机制与设计思想**

线程池的实现原理

当向线程池提交一个任务之后,线程池是如何处理这个任务的呢?根据刚刚讲的线程池参数的含义,我们来看一下线程池 的主要处理流程。

从图中可以看出,当提交一个新任务到线程池时,线程池的处理流程是这样的, 这个很关键,面试必问。

在这里插入图片描述

  1. 判断核心线程池是否已满,即线程数是否达到corePoolSize

如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程已经满了,则进入下个流程。

  • 判断工作队列是否已经满。 BlockingQueue

如果工作队列没有满,则将新提交的任务存储在这 个工作队列里。如果工作队列满了,则进入下个流程。

  • 判断线程池是否已满,即线程数是否达到maxPoolSize

如果没有,则创建一个新的工作线程 来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

再来看看 ThreadPoolExecutor执行execute()方法的图:

在这里插入图片描述

按照我们上面说的, ThreadPoolExecutor执行execute方法也会分为这几种情况。

  1. 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

  2. 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。

  3. 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

  4. 如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用 RejectedExecutionHandler.rejectedExecution()方法。

ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁, 因为很明显这是一个严重的瓶颈。

在ThreadPoolExecutor完成预热之后 , 也就是当前运行的线程数大于等于corePoolSize,几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。

通过流程分析,我们很直观地了解了线程池的工作原理,接下来, 我们再通过源代码来看看具体是如何实现的

execute(Runnable command)

//高3位表示状态,低29位表示线程数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {//如果当前工作线程数小于核心线程数(corePoolSize),//则尝试创建新线程作为核心线程并立即执行任务if (addWorker(command, true)) // 以核心模式创建Workerreturn;c = ctl.get(); // 若创建失败(如线程池已关闭),重新获取ctl值}if (isRunning(c) && workQueue.offer(command)) { // 检查状态并尝试入队int recheck = ctl.get();if (! isRunning(recheck) && remove(command))// 线程池已关闭且任务成功移除reject(command); // 拒绝任务else if (workerCountOf(recheck) == 0) //无存活线程addWorker(null, false); //创建非核心线程防止队列任务积压}else if (!addWorker(command, false))// 以非核心模式创建Workerreject(command); // 抛出RejectedExecutionException异常
}

1. 阶段一:尝试创建核心线程

int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true)) // 以核心模式创建Workerreturn;c = ctl.get(); // 若创建失败(如线程池已关闭),重新获取ctl值
}
  • 目标:如果当前工作线程数小于核心线程数(corePoolSize),则尝试创建新线程作为核心线程并立即执行任务。

  • 关键操作

    • workerCountOf(c):解析 ctl 变量(高3位表示状态,低29位表示线程数)获取当前工作线程数。

    • addWorker(command, true):尝试以核心线程限制(corePoolSize)创建工作者(Worker),command 作为首任务。

    • 失败处理:若线程池已关闭(状态非 RUNNING)或并发冲突导致创建失败,则进入阶段二。

2. 阶段二:尝试将任务加入队列

if (isRunning(c) && workQueue.offer(command)) { // 检查状态并尝试入队int recheck = ctl.get(); // 重新获取状态以应对并发变化if (!isRunning(recheck) && remove(command)) // 线程池已关闭且任务成功移除reject(command); // 拒绝任务else if (workerCountOf(recheck) == 0) // 无存活线程(例如核心线程超时被回收)addWorker(null, false); // 创建非核心线程(后续从队列取任务)
}
  • 目标:当核心线程已满,尝试将任务加入阻塞队列(如 LinkedBlockingQueue)。

  • 关键操作

    • 双重状态检查

      1. 初始入队前校验线程池是否处于运行状态(isRunning(c))。

      2. 入队后再次校验(recheck),避免在入队期间线程池被关闭。

    • 处理极端情况

      • 若线程池已关闭且成功从队列移除任务,则触发拒绝策略。

      • 若所有工作线程意外终止(例如异常退出),则新建非核心线程(参数 firstTask = null),强制处理队列中的积压任务。此处的 null 表示新线程不立即执行提交的command,而是直接从队列中获取任务(通过 Worker.run() 中的循环逻辑)。此时创建的非核心线程虽然无初始任务,但会主动消费队列中积累的任务,确保队列不积压。

      while (running) {Job job = null;synchronized (jobs) {if (jobs.isEmpty()) jobs.wait(); // 从队列取任务job = jobs.removeFirst();}if (job != null) job.run();
      }
      

3. 阶段三:尝试创建非核心线程或拒绝任务

else if (!addWorker(command, false)) // 以非核心模式创建Workerreject(command); // 队列已满且线程数达到maximumPoolSize,拒绝任务
  • 目标:当队列已满时,尝试创建非核心线程(线程数上限为 maximumPoolSize)。

  • 关键操作

    • addWorker(command, false):以非核心模式创建工作者,直接执行当前任务。

    • 失败条件

      • 线程数已达 maximumPoolSize

      • 线程池已关闭(非 RUNNING 状态)。

    • 拒绝策略:调用 RejectedExecutionHandler.rejectedExecution(),默认抛出 RejectedExecutionException

关键机制与设计思想

  1. 全局锁(Global Lock)的应用

    • addWorker() 方法内部通过锁(如 ReentrantLock)同步线程池状态修改操作(例如增减线程、更新 ctl),确保原子性。
  2. 减少锁竞争优化

    • 在核心线程已预热的情况下,多数任务直接通过队列处理(阶段二),避免了频繁获取锁的性能损耗。
  1. Worker执行逻辑(补充)

    • 每个 Worker 启动后执行 runWorker() 方法,循环从队列中获取任务。

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

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

相关文章

清华大学开源软件镜像站地址

清华大学开源软件镜像站&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/

脑机接口技术:开启人类与机器融合的新时代

摘要 脑机接口&#xff08;BCI&#xff09;技术作为一项前沿科技&#xff0c;正在逐步打破人类与机器之间的沟通障碍&#xff0c;为医疗、娱乐、教育等多个领域带来前所未有的变革。本文将详细介绍脑机接口技术的基本原理、发展现状、应用场景以及面临的挑战和未来发展趋势&…

2025前端面试遇到的问题(vue+uniapp+js+css)

Vue相关面试题 vue2和vue3的区别 一、核心架构差异 特性Vue2Vue3响应式系统基于Object.defineProperty基于Proxy&#xff08;支持动态新增/删除属性&#xff09;代码组织方式Options API&#xff08;data/methods分块&#xff09;Composition API&#xff08;逻辑按功能聚合&am…

Matlab基于SSA-MVMD麻雀算法优化多元变分模态分解

Matlab基于SSA-MVMD麻雀算法优化多元变分模态分解 目录 Matlab基于SSA-MVMD麻雀算法优化多元变分模态分解效果一览基本介绍程序设计参考资料效果一览 基本介绍 Matlab基于SSA-MVMD麻雀算法优化多元变分模态分解 可直接运行 分解效果好 适合作为创新点(Matlab完整源码和数据),…

Gatsby知识框架

一、Gatsby 基础概念 1. 核心特性 基于React的静态站点生成器&#xff1a;使用React构建&#xff0c;输出静态HTML/CSS/JS GraphQL数据层&#xff1a;统一的数据查询接口 丰富的插件系统&#xff1a;超过2000个官方和社区插件 高性能优化&#xff1a;自动代码分割、预加载、…

【论信息系统项目的资源管理】

论信息系统项目的资源管理 前言一、规划好资源管理&#xff0c;为保证项目完成做好人员规划二、估算活动资源&#xff0c;为制订项目进度计划提供资源需求三、获取项目资源&#xff0c;组建一个完备的项目团队四、建设项目团队&#xff0c;提高工作能力&#xff0c;促进团队成员…

idea Maven 打包SpringBoot可执行的jar包

背景&#xff1a;当我们需要坐联调测试的时候&#xff0c;需要对接前端同事&#xff0c;则需要打包成jar包直接运行启动服务 需要将项目中的pom文件增加如下代码配置&#xff1a; <build><plugins><plugin><groupId>org.springframework.boot</gr…

VScode 的插件本地更改后怎么生效

首先 vscode 的插件安装地址为 C:\Users\%USERNAME%\.vscode\extensions 找到你的插件包进行更改 想要打印日志&#xff0c;用下面方法 vscode.window.showErrorMessage(console.log "${name}" exists.); 打印结果 找到插件&#xff0c;点击卸载 然后点击重新启动 …

Python训练营打卡——DAY24(2025.5.13)

目录 一、元组 1. 通俗解释 2. 元组的特点 3. 元组的创建 4. 元组的常见用法 二、可迭代对象 1. 定义 2. 示例 3. 通俗解释 三、OS 模块 1. 通俗解释 2. 目录树 四、作业 1. 准备工作 2. 实战代码示例​ 3. 重要概念解析 一、元组 是什么​​&#xff1a;一种…

初入OpenCV

OpenCV简介 OpenCV是一个开源的跨平台计算机视觉库&#xff0c;它实现了图像处理和计算机视觉方面的很多通用算法。 应用场景&#xff1a; 目标识别&#xff1a;人脸、车辆、车牌、动物&#xff1b; 自动驾驶&#xff1b;医学影像分析&#xff1b; 视频内容理解分析&#xff…

讯联云库项目开发日志(一)

1、设计数据库 2、写基本框架 entity、controller、service、exception、utils、mapper mapper层&#xff1a; 生成了一系列的CRUD方法 工具类&#xff1a;线程安全的日期工具类、 ​​参数校验工具类​ 线程安全的日期工具类​​&#xff1a;主要用于 ​​日期格式化&…

langchain学习

无门槛免费申请OpenAI ChatGPT API搭建自己的ChatGPT聊天工具 https://nuowa.net/872 基本概念 LangChain 能解决大模型的两个痛点&#xff0c;包括模型接口复杂、输入长度受限离不开自己精心设计的模块。根据LangChain 的最新文档&#xff0c;目前在 LangChain 中一共有六大…

Protobuf工具

#region 知识点一 什么是 Protobuf //Protobuf 全称是 protocol - buffers&#xff08;协议缓冲区&#xff09; // 是谷歌提供给开发者的一个开源的协议生成工具 // 它的主要工作原理和我们之前做的自定义协议工具类似 // 只不过它更加的完善&…

zst-2001 上午题-历年真题 软件工程(38个内容)

CMM 软件工程 - 第1题 b 软件工程 - 第2题 c 软件工程 - 第3题 c 软件工程 - 第4题 b 软件工程 - 第5题 b CMMI 软件工程 - 第6题 0.未完成&#xff1a;未执行未得到目标。1.已执行&#xff1a;输入-输出实现支持2.已管理&#xff1a;过程制度化&#x…

软考架构师考试-UML图总结

考点 选择题 2-4分 案例分析0~1题和面向对象结合考察&#xff0c;前几年固定一题。近3次考试没有出现。但还是有可能考。 UML图概述 1.用例图&#xff1a;描述系统功能需求和用户&#xff08;参与者&#xff09;与系统之间的交互关系&#xff0c;聚焦于“做什么”。 2.类图&…

数据结构(七)——图

一、图的定义与基本术语 1.图的定义 图G由顶点集V和边集E组成&#xff0c;记为G(V,E)&#xff0c;其中V(G)表示图G中顶点的有限非空集&#xff1b;E(G)表示图G中顶点之间的关系&#xff08;边&#xff09;的集合 注意&#xff1a;线性表可以是空表&#xff0c;树可以是空树&…

Android7 Input(六)InputChannel

概述: 本文讲述Android Input输入框架中 InputChannel的功能。从前面的讲述&#xff0c;我们知道input系统服务最终将输入事件写入了InputChannel&#xff0c;而input属于system_server进程&#xff0c;App属于另外一个进程&#xff0c;当Input系统服务想要把事件传递给App进行…

【 Redis | 实战篇 秒杀实现 】

目录 前言&#xff1a; 1.全局ID生成器 2.秒杀优惠券 2.1.秒杀优惠券的基本实现 2.2.超卖问题 2.3.解决超卖问题的方案 2.4.基于乐观锁来解决超卖问题 3.秒杀一人一单 3.1.秒杀一人一单的基本实现 3.2.单机模式下的线程安全问题 3.3.集群模式下的线程安全问题 前言&…

如何用URDF文件构建机械手模型并与MoveIt集成

机械手URDF文件的编写 我们用urdf文件来描述我们的机械手的外观以及物理性能。这里为了简便&#xff0c;就只用了基本的圆柱、立方体了。追求美观的朋友&#xff0c;还可以用dae文件来描述机械手的外形。 import re def remove_comments(text):pattern r<!--(.*?)-->…

《构建社交应用的安全结界:双框架对接审核API的底层逻辑与实践》

用户生成内容如潮水般涌来。从日常的生活分享&#xff0c;到激烈的观点碰撞&#xff0c;这些内容赋予社交应用活力&#xff0c;也带来管理难题。虚假信息、暴力言论、侵权内容等不良信息&#xff0c;如同潜藏的暗礁&#xff0c;威胁着社交平台的健康生态。内容审核机制&#xf…