7天吃透Java多线程!线程池+并发工具类实战,解锁企业级开发技能
作为Java开发者,你是否也曾在高并发场景中踩过这些坑?手动new Thread()创建线程导致服务器资源耗尽,多线程竞争共享资源引发数据错乱,复杂的线程协作逻辑写得头晕脑胀还容易出bug……
多线程是Java进阶路上绕不开的核心技能,也是大厂面试的高频考点。经过7天的系统深耕,我们从线程基础、创建方式、执行控制,到同步锁机制、线程通信,一步步搭建起完整的知识体系。而今天,我们将聚焦企业级开发的“效率神器”——线程池与并发工具类,用实战代码带你告别手动线程管理,真正实现多线程开发的“降本增效”。
一、为什么线程池是高并发的“救命稻草”?
手动管理线程的3大致命痛点,你一定深有体会:
- 资源开销大:线程创建/销毁消耗CPU和内存,高并发下频繁操作直接拖垮系统;
- 缺乏管控:无限制创建线程可能触发
OutOfMemoryError,系统直接崩溃; - 无复用机制:线程执行完任务就销毁,重复创建造成巨大资源浪费。
而线程池的核心价值就是“线程复用+统一管控”:提前创建一批线程待命,任务到来时直接分配线程执行,任务结束后线程归池复用,彻底解决手动管理的痛点。
二、线程池核心原理:ThreadPoolExecutor七大参数(附实战代码)
Java线程池的核心实现类是ThreadPoolExecutor,掌握它的七大参数,才算真正懂线程池。直接上源码和实战案例,边看边学:
1. 七大参数源码解析
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数(长期存活,除非设置allowCoreThreadTimeOut)
int maximumPoolSize, // 最大线程数(核心+非核心线程上限)
long keepAliveTime, // 非核心线程空闲存活时间
TimeUnit unit, // 存活时间单位(如SECONDS、MILLISECONDS)
BlockingQueue<Runnable> workQueue, // 任务阻塞队列(核心线程满时存任务)
ThreadFactory threadFactory, // 线程工厂(自定义线程名称、优先级等)
RejectedExecutionHandler handler // 任务拒绝策略(队列+最大线程满时触发)
)
2. 关键参数实战:百万级数据清洗
用自定义线程池处理百万级数据清洗任务,代码可直接复制到项目中使用:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
// 数据清洗任务:过滤负数,保留正数
class DataCleanTask implements Runnable {
private List<Integer> dataBatch;
private int taskId;
public DataCleanTask(List<Integer> dataBatch, int taskId) {
this.dataBatch = dataBatch;
this.taskId = taskId;
}
@Override
public void run() {
try {
List<Integer> cleanData = new ArrayList<>();
for (Integer num : dataBatch) {
if (num > 0) { // 清洗规则:过滤负数
cleanData.add(num);
}
}
System.out.println("任务" + taskId + "清洗完成,原数据量:" + dataBatch.size() + ",清洗后:" + cleanData.size());
} catch (Exception e) {
System.out.println("任务" + taskId + "清洗异常");
e.printStackTrace();
}
}
}
public class CustomThreadPoolDemo {
public static void main(String[] args) {
// 1. 初始化百万级测试数据(-10万到89万,共100万条)
List<Integer> dataList = new ArrayList<>();
for (int i = -100000; i < 900000; i++) {
dataList.add(i);
}
// 2. 自定义线程池(核心参数实战配置)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // 核心线程数4(长期存活)
8, // 最大线程数8(核心4+非核心4)
60, // 非核心线程空闲60秒销毁
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10), // 有界队列,容量10(避免内存溢出)
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:提交线程自行执行
);
// 3. 拆分数据为批次,提交任务(每批10万条)
int batchSize = 100000;
int taskId = 1;
for (int i = 0; i < dataList.size(); i += batchSize) {
int end = Math.min(i + batchSize, dataList.size());
List<Integer> batch = dataList.subList(i, end);
executor.submit(new DataCleanTask(batch, taskId++));
}
// 4. 优雅关闭线程池(关键!避免任务丢失)
executor.shutdown();
try {
// 等待10分钟,若未完成则强制关闭
if (!executor.awaitTermination(10, TimeUnit.MINUTES)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
e.printStackTrace();
}
System.out.println("所有数据清洗任务执行完毕");
}
}
3. 执行效果解析
- 核心线程先启动4个处理任务,队列满(10个任务)后创建非核心线程;
- 任务完成后,非核心线程空闲60秒自动销毁,避免资源浪费;
- 有界队列+合理拒绝策略,防止任务过多导致内存溢出。
三、4种常用线程池:Executors工具类快速上手
JDK的Executors工具类封装了4种高频线程池,无需手动配置,一行代码即可使用:
1. FixedThreadPool(固定线程池)
// 核心线程数=最大线程数,无非核心线程,适用于稳定长期任务
ExecutorService fixedExecutor = Executors.newFixedThreadPool(5);
2. CachedThreadPool(缓存线程池)
// 核心线程数0,最大线程数无界,适用于短期突发任务
ExecutorService cachedExecutor = Executors.newCachedThreadPool();
3. SingleThreadExecutor(单线程池)
// 线程数=1,任务串行执行,适用于日志写入、订单处理等有序场景
ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
4. ScheduledThreadPool(定时线程池)
// 支持定时/周期性任务,适用于定时备份、定时统计
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(3);
// 延迟1秒执行,每隔3秒重复一次
scheduledExecutor.scheduleAtFixedRate(() -> System.out.println("定时任务执行"), 1, 3, TimeUnit.SECONDS);
⚠️ 注意:FixedThreadPool和SingleThreadExecutor默认使用无界队列,任务过多可能引发内存溢出,实际开发建议自定义ThreadPoolExecutor+有界队列。
四、并发工具类:CountDownLatch与CyclicBarrier(实战代码)
多线程协作不用愁,这两个工具类帮你简化逻辑:
1. CountDownLatch:主线程等待子线程完成
场景:多线程读取多个文件,所有文件读取完毕后汇总结果
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
// 文件读取任务
class FileBatchReadTask implements Runnable {
private String filePath;
private CountDownLatch latch;
private int lineCount = 0;
public FileBatchReadTask(String filePath, CountDownLatch latch) {
this.filePath = filePath;
this.latch = latch;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(filePath));
while (br.readLine() != null) {
lineCount++;
}
System.out.println(Thread.currentThread().getName() + " 读取文件" + filePath + ",总行数:" + lineCount);
} catch (IOException e) {
System.out.println("文件读取失败");
e.printStackTrace();
} finally {
if (br != null) try { br.close(); } catch (IOException e) {}
latch.countDown(); // 任务完成,计数器减1
}
}
public int getLineCount() { return lineCount; }
}
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
String[] files = {"file1.txt", "file2.txt", "file3.txt"};
CountDownLatch latch = new CountDownLatch(files.length); // 计数器初始值=文件数
FileBatchReadTask[] tasks = new FileBatchReadTask[files.length];
// 提交任务到线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < files.length; i++) {
tasks[i] = new FileBatchReadTask(files[i], latch);
executor.submit(tasks[i]);
}
latch.await(); // 主线程等待,直到计数器归零
System.out.println("所有文件读取完毕,开始汇总");
// 汇总总行数
int totalLine = 0;
for (FileBatchReadTask task : tasks) {
totalLine += task.getLineCount();
}
System.out.println("文件总行数:" + totalLine);
executor.shutdown();
}
}
2. CyclicBarrier:线程同步屏障,同时继续执行
场景:多线程数据计算,所有线程完成预处理后,同时进入计算阶段
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 数据计算任务
class DataCalcTask implements Runnable {
private CyclicBarrier barrier;
private int taskId;
private int result;
public DataCalcTask(CyclicBarrier barrier, int taskId) {
this.barrier = barrier;
this.taskId = taskId;
}
@Override
public void run() {
try {
// 阶段1:数据预处理
System.out.println("任务" + taskId + " 开始数据预处理");
Thread.sleep(1000);
System.out.println("任务" + taskId + " 预处理完成,等待其他任务");
// 等待所有任务到达屏障
barrier.await();
// 阶段2:统一计算
result = taskId * 100;
System.out.println("任务" + taskId + " 计算完成,结果:" + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) {
// 3个任务+屏障处执行的汇总操作
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("===== 所有任务预处理完成,进入计算阶段 =====");
});
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 3; i++) {
executor.submit(new DataCalcTask(barrier, i));
}
executor.shutdown();
}
}
五、综合实战:多线程下载器(线程池+IO+同步锁)
整合所有知识点,实现企业级多线程下载器,支持文件分段下载,效率翻倍:
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
// 分段下载任务
class SegmentDownloadTask implements Runnable {
private String url;
private String savePath;
private long start;
private long end;
private int segmentId;
private CountDownLatch latch;
private ReentrantLock lock;
public SegmentDownloadTask(String url, String savePath, long start, long end, int segmentId, CountDownLatch latch, ReentrantLock lock) {
this.url = url;
this.savePath = savePath;
this.start = start;
this.end = end;
this.segmentId = segmentId;
this.latch = latch;
this.lock = lock;
}
@Override
public void run() {
HttpURLConnection conn = null;
InputStream in = null;
RandomAccessFile raf = null;
try {
// 建立HTTP连接,设置分段请求
URL resourceUrl = new URL(url);
conn = (HttpURLConnection) resourceUrl.openConnection();
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
conn.connect();
// 读取分段数据并写入文件
in = conn.getInputStream();
raf = new RandomAccessFile(savePath, "rw");
raf.seek(start); // 定位到分段起始位置
byte[] buffer = new byte[1024 * 8];
int len;
while ((len = in.read(buffer)) != -1) {
lock.lock(); // 加锁保证写入安全
try {
raf.write(buffer, 0, len);
} finally {
lock.unlock(); // 必须释放锁
}
}
System.out.println("分段" + segmentId + "下载完成,范围:" + start + "-" + end);
} catch (IOException e) {
System.out.println("分段" + segmentId + "下载失败");
e.printStackTrace();
} finally {
// 关闭资源
if (in != null) try { in.close(); } catch (IOException e) {}
if (raf != null) try { raf.close(); } catch (IOException e) {}
if (conn != null) conn.disconnect();
latch.countDown(); // 任务完成,计数器减1
}
}
}
// 多线程下载器主类
public class MultiThreadDownloader {
public static void main(String[] args) throws IOException, InterruptedException {
// 下载配置
String downloadUrl = "https://example.com/largeFile.zip"; // 目标文件URL
String savePath = "largeFile.zip"; // 保存路径
int threadNum = 5; // 5个线程同时下载
// 1. 获取文件总大小
URL url = new URL(downloadUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
long fileSize = conn.getContentLengthLong();
conn.disconnect();
System.out.println("文件总大小:" + fileSize + "字节");
// 2. 计算分段大小
long segmentSize = fileSize / threadNum;
CountDownLatch latch = new CountDownLatch(threadNum);
ReentrantLock lock = new ReentrantLock(); // 保证文件写入安全
// 3. 初始化线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
threadNum, threadNum, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
);
// 4. 分配分段任务
for (int i = 0; i < threadNum; i++) {
long start = i * segmentSize;
long end = (i == threadNum - 1) ? fileSize - 1 : (i + 1) * segmentSize - 1;
executor.submit(new SegmentDownloadTask(downloadUrl, savePath, start, end, i + 1, latch, lock));
}
// 5. 等待所有分段下载完成
System.out.println("等待所有分段下载完成...");
latch.await();
executor.shutdown();
System.out.println("文件下载完成,保存路径:" + savePath);
}
}
六、这些干货,只给关注的粉丝
7天的多线程学习只是起点,上述所有完整源码、注释详解、扩展案例,我都整理成了「Java多线程实战手册」,包含:
- 线程池7大参数深度解析+避坑指南;
- 4种线程池+2种并发工具类完整案例;
- 多线程下载器、数据清洗、日志分析等实战项目源码;
- 大厂面试高频多线程真题解析。
关注GZH「咖啡 java 研习班」,回复关键词「学习资料」即可免费领取!
七、后续学习预告&实战作业
1. 后续学习计划
- 底层深挖:JVM并发原理、锁的底层实现、线程上下文切换;
- 企业级开发:JavaWeb+SpringBoot整合多线程,解决实际业务问题;
- 分布式并发:分布式锁、线程池监控、高并发系统设计。
2. 实战小作业
用线程池+CyclicBarrier+IO流实现「多线程日志分析系统」:
- 多线程同时读取不同日志文件;
- 所有文件读取完成后,统一分析错误日志;
- 分析结果写入汇总报告;
- 加入异常处理保障稳定性。
完成后可在公众号后台提交代码,我会逐一点评,优秀作品还会被收录到实战案例库!
多线程是Java进阶的“分水岭”,掌握它不仅能搞定面试,更能解决工作中的高并发难题。关注「咖啡 java 研习班」,跟着实战案例学Java,让技术成长少走弯路~
👇 扫码关注,领取多线程实战手册 👇 直接搜索GZH名称「咖啡 java 研习班」,回复「学习资料」即可领取完整源码和学习资料~