Java并发编程实战-多线程任务执行
Executor框架与线程池(ThreadPoolExecutor)
Executor框架的组成
组件 | 作用 |
---|---|
Executor |
基础接口,仅定义execute(Runnable) 方法,用于执行任务。 |
ExecutorService |
扩展Executor ,提供任务提交(submit )、线程池关闭(shutdown )等功能。 |
ScheduledExecutorService |
支持定时或周期性任务调度。 |
ThreadPoolExecutor |
最常用的线程池实现类,提供高度可配置的线程池管理。 |
Executors |
工厂类,提供创建常见线程池的快捷方法(如newFixedThreadPool )。 |
ThreadPoolExecutor的核心机制
线程池的构造参数
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
任务提交
- 若当前线程数 <
corePoolSize
,立即创建新线程执行任务。 - 若线程数 ≥
corePoolSize
,将任务放入workQueue
等待。 - 若队列已满且线程数 <
maximumPoolSize
,创建临时线程处理任务。 - 若队列已满且线程数 ≥
maximumPoolSize
,触发RejectedExecutionHandler
拒绝任务。
- 若当前线程数 <
线程回收
- 临时线程(超过
corePoolSize
的部分)在空闲keepAliveTime
后被回收。 - 核心线程默认不回收,可通过
allowCoreThreadTimeOut(true)
启用回收。
- 临时线程(超过
关键参数详解
参数 作用与典型值 corePoolSize 核心线程数(常驻线程),默认长期存活。CPU密集型任务建议设为 CPU核心数+1
。maximumPoolSize 最大线程数(含核心线程)。IO密集型任务可设较高值(如 2*CPU核心数
)。keepAliveTime 临时线程空闲存活时间(如30秒)。 workQueue 任务队列,具体实现: LinkedBlockingQueue
:无界队列(需防OOM)ArrayBlockingQueue
:有界队列(需合理容量)SynchronousQueue
:直接传递队列(高吞吐)threadFactory 自定义线程创建逻辑(如命名线程、设置优先级)。 handler 拒绝策略(当队列和线程池全满时处理新任务): AbortPolicy
(默认):抛出RejectedExecutionException
CallerRunsPolicy
:由提交任务的线程执行任务DiscardPolicy
:静默丢弃任务DiscardOldestPolicy
:丢弃队列最旧任务并重试提交CorePoolSize设置
CPU密集型任务(如计算、压缩):
- 核心线程数 = CPU核心数 + 1
- 公式:
corePoolSize = N + 1
(N为CPU核心数),避免线程竞争导致的上下文切换开销。
I/O密集型任务(如网络请求、数据库查询):
- 核心线程数 = CPU核心数 × 2
- 公式:
corePoolSize = N × (1 + 平均I/O等待时间 / 平均计算时间)
,若无法精确统计,可设为2N
~5N
。
线程池的创建与使用
方法 | 实现类 | 特性 |
---|---|---|
newFixedThreadPool(n) |
ThreadPoolExecutor | 固定大小线程池,无界队列(LinkedBlockingQueue ),核心线程数=最大线程数。 |
newCachedThreadPool() |
ThreadPoolExecutor | 可扩容线程池,最大线程数为Integer.MAX_VALUE ,使用SynchronousQueue 。 |
newSingleThreadExecutor() |
ThreadPoolExecutor | 单线程池,任务按顺序执行,无界队列。 |
newScheduledThreadPool(n) |
ScheduledThreadPoolExecutor | 支持定时任务调度。 |
任务分界与执行策略
任务分界的原则
独立性
- 无共享状态:任务之间尽量不依赖共享数据,避免同步开销。
- 示例:将大数组分割为多个子数组,分别计算子数组的和。
粒度平衡
- 粗粒度:任务过少,无法充分利用多核资源。
- 细粒度:任务过多,线程管理开销增大。
- 目标:根据硬件(如CPU核心数)和任务类型(CPU/IO密集型)调整任务粒度。
可组合性:支持将子任务结果合并为最终结果(如
ForkJoinPool
的RecursiveTask
)。
任务分界的方法
数据分片(Data Partitioning)
- 原理:将数据集划分为多个子集,每个线程处理一个子集。
- 适用场景:批量数据处理(如统计、图像渲染)。
任务流水线(Pipeline)
- 原理:将任务拆分为多个阶段,每个阶段由独立线程处理(类似工厂流水线)。
- 适用场景:多阶段处理(如日志采集 → 清洗 → 存储)。
递归分解(Recursive Decomposition)
- 原理:将任务递归拆分为更小的子任务,直到达到阈值后顺序执行。
- 适用场景:分治算法(如归并排序、快速排序)。
执行策略的类型与选择
并行处理(Parallel Processing)
- 核心思想:将任务分配给多个线程同时执行,充分利用多核CPU。
- 适用场景:计算密集型任务(如数值计算、图像处理)。
异步执行(Asynchronous Execution)
- 核心思想:任务提交后立即返回,通过回调或
Future
获取结果,不阻塞主线程。 - 适用场景:IO密集型任务(如网络请求、文件读写)。
- 实现工具:
CompletableFuture
(链式异步编程)、Future
+ExecutorService
。
- 核心思想:任务提交后立即返回,通过回调或
混合策略
- 接收请求:使用异步IO(如NIO)非阻塞接收连接。
- 处理请求:CPU密集型任务交给固定线程池,IO操作使用异步回调。
执行策略的调优技巧
线程池配置
- 计算密集型:线程数 ≈ CPU核心数(避免过多线程竞争CPU)。
- IO密集型:线程数 ≈ CPU核心数 × (1 + 平均IO等待时间/计算时间)。
- 动态调整:根据监控指标(队列长度、活跃线程数)调整线程池参数。
任务队列选择
- 无界队列(如
LinkedBlockingQueue
):适合任务量不可控但需快速响应。 - 有界队列(如
ArrayBlockingQueue
):避免内存溢出,需配合拒绝策略。 - 直接传递队列(
SynchronousQueue
):高吞吐,要求线程池足够大。
- 无界队列(如
拒绝策略
AbortPolicy
:直接抛出异常,默认策略。CallerRunsPolicy
:由提交任务的线程执行任务,降低提交速率。DiscardOldestPolicy
:丢弃队列中最旧的任务,腾出空间。- 自定义策略:记录日志或持久化被拒绝的任务。
任务取消的机制
Future.cancel()
方法定义
boolean cancel(boolean mayInterruptIfRunning);
作用:尝试取消任务的执行。
参数:
mayInterruptIfRunning
表示是否允许中断正在执行任务的线程。返回值:
true
:任务成功取消(任务未启动或已启动且允许中断)。false
:任务已结束或无法取消。
cancel()
的行为场景
任务尚未启动:任务直接从任务队列中移除,不会执行。
任务正在运行:根据
mayInterruptIfRunning
参数决定是否中断任务线程:mayInterruptIfRunning=true
:向任务线程发送中断信号(设置中断标志位)。mayInterruptIfRunning=false
:不中断线程,任务继续执行,但若任务未开始则取消。
任务已完成或已取消:调用
cancel()
返回false
,无实际效果。
任务如何响应取消
协作式中断:任务的取消需要协作,即任务必须主动检查中断状态或处理中断异常。
- 检查中断标志:在循环或耗时操作中定期调用
Thread.interrupted()
。 - 处理
InterruptedException
:在阻塞操作(如Thread.sleep()
、wait()
)中捕获异常并退出。
- 检查中断标志:在循环或耗时操作中定期调用
与其他机制对比
机制 | 原理 | 优点 | 缺点 |
---|---|---|---|
Future.cancel() |
基于中断标志的协作式取消 | 标准化,与线程池集成方便 | 依赖任务协作,无法强制终止 |
自定义标志位 | 通过volatile boolean 控制 |
灵活,无中断依赖 | 需手动检查,无法中断阻塞操作 |
Thread.stop() |
强制终止线程(已废弃) | 立即停止任务 | 不安全,易导致数据不一致 |
Java并发编程实战-多线程任务执行的更多相关文章
- Java并发编程实战---第六章:任务执行
废话开篇 今天开始学习Java并发编程实战,很多大牛都推荐,所以为了能在并发编程的道路上留下点书本上的知识,所以也就有了这篇博文.今天主要学习的是任务执行章节,主要讲了任务执行定义.Executor. ...
- 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结
<Java并发编程实战>和<Java并发编程的艺术> Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...
- 【Java并发编程实战】-----“J.U.C”:CyclicBarrier
在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock
ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...
- 【java并发编程实战】-----线程基本概念
学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习 ...
- 《java并发编程实战》笔记
<java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为: Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...
- 《Java并发编程实战》文摘
更新时间:2017-06-03 <Java并发编程实战>文摘,有兴趣的朋友可以买本纸质书仔细研究下. 一 线程安全性 1.1 什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何 ...
- 《Java并发编程实战》读书笔记一 -- 简介
<Java并发编程实战>读书笔记一 -- 简介 并发的历史 并发的历史,也是人类利用有限的资源去提高生产效率的一个的例子. 设想现在有台计算机,这台计算机具有以下的资源: 单核CPU一个 ...
- Java并发编程实战 01并发编程的Bug源头
摘要 编写正确的并发程序对我来说是一件极其困难的事情,由于知识不足,只知道synchronized这个修饰符进行同步. 本文为学习极客时间:Java并发编程实战 01的总结,文章取图也是来自于该文章 ...
- 【Java并发编程实战】----- AQS(四):CLH同步队列
在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...
随机推荐
- java注释转json插件开发实战
目的 将java的代码注释转换为json格式,并写入文件 本文介绍了完整的开发流程及如何使用 运行环境 jdk1.8 maven3.x 设计思想 系统构思 编译完成的class里没有注释的,所以注释信 ...
- [业界方案] ClickHouse业界解决方案学习笔记
[业界方案] ClickHouse业界解决方案学习笔记 目录 [业界方案] ClickHouse业界解决方案学习笔记 0x00 摘要 0x01 简介 0x02 OLAP场景的特点 0x03 选型原因 ...
- Q:LISTAGG()函数用法笔记(oracle)
.LISTAGG()函数作为普通函数使用时就是查询出来的结果列转为行 ☆LISTAGG 函数既是分析函数,也是聚合函数有两种用法:1.分析函数,如: row_number().rank().dense ...
- Linux mint安装百度云
deb包下载 BCloud 作者官网https://github.com/XuShaohua/bcloud-packages 链接: https://pan.baidu.com/s/1hskY04G ...
- Linux安装nodejs npm
1.检查 whereis nodejs whereis npm 2.下载 wget -c https://npm.taobao.org/mirrors/node/v12.12.0/node-v12.1 ...
- Flink程序异常--CommunicationsException: The last packet successfully received from the server was
一.异常截图 com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully receive ...
- Vue3+NestJS实现后台权限管理系统上线啦!(附源码及教程)
最近这段时间工作不忙,想着提升一下自己的技术,沉淀沉淀.于是做了一个开源的后台权限管理系统.因为我本身是一个前端开发,所以前端和服务端都是用的 JS 语言来开发的,前端用的框架是 vue3,后端则用的 ...
- IPMITool 工具使用详细教程
IPMITool 工具使用详细教程 一.IPMI 与 IPMITool 简介 1. IPMI 概述 智能平台管理接口(Intelligent Platform Management Interface ...
- Ubuntu 部署饥荒联机版服务器 Linux DST_Dedicate_Server
0. 文件夹 - ~ |- ~/steamcmd # 装的是steamcmd_linux.tar.gz以及其解压出来的东西 |- ~/DST # 装的是DST服务器可执行文件.世界存档.世界模板 |- ...
- Flink - [05] 时间语义 & Watermark
题记部分 一.时间语义 Flink中的时间语义分为以下, (1)Event Time:事件创建的时间 (2)Ingestion Time:数据进入Flink的时间 (3)Processing Time ...