Java****线程池详解
- 线程池基础
1.1 什么是线程池
1.2 为什么需要线程池
1.3 线程池的核心参数
1.4 线程池工作原理
- Java中的线程池类型与使用
2.1 常见的线程池类型
2.1.1 FixedThreadPool
2.1.2 CachedThreadPool
2.1.3 SingleThreadExecutor
2.1.4 ScheduledThreadPool
2.2 线程池的使用示例
2.2.1 FixedThreadPool的使用
2.2.2 CachedThreadPool的使用
2.2.3 SingleThreadExecutor的使用
2.2.4 ScheduledThreadPool的使用
2.3 线程池的优缺点
2.3.1 线程池的优点
2.3.2 线程池的缺点
- ThreadPoolExecutor详解
3.1 ThreadPoolExecutor的生命周期
3.2 ThreadPoolExecutor执行流程
3.3 线程池参数调优
3.3.1 核心线程数和最大线程数
3.3.2 工作队列的选择
3.3.3 拒绝策略的选择
- 阻塞队列与线程池的关系
4.1 常用阻塞队列类型
4.1.1 ArrayBlockingQueue
4.1.2 LinkedBlockingQueue
4.1.3 SynchronousQueue
4.1.4 PriorityBlockingQueue
4.1.5 DelayQueue
4.2 阻塞队列对线程池行为的影响
4.2.1 有界队列
4.2.2 无界队列
4.2.3 同步队列
4.3 队列选择指南
4.4 阻塞队列性能对比
- 线程池监控与管理
5.1 内置监控功能
5.2 JMX监控
5.3 自定义线程池监控器
5.4 动态调整线程池配置
5.5 线程池监控最佳实践
- 线程池常见问题与解决方案
6.1 线程池死锁与死锁检测
解决方案:
6.2 任务拒绝处理
自定义拒绝策略
6.3 线程泄漏问题场景一:忘记关闭线程池
场景二:线程被阻塞且无法中断
6.4 线程池性能问题与调优
常见性能问题
性能调优策略
6.5 在容器环境中使用线程池的注意事项
问题
解决方案
- 线程池最佳实践
7.1 线程池创建与配置最佳实践
避免使用Executors工厂方法
线程池命名
线程池分离原则
7.2 任务提交最佳实践
使用合适的任务提交方法
避免任务依赖
设置任务超时
推荐使用CompletableFuture
7.3 异常处理最佳实践
处理未捕获的异常
异常统计与监控
7.4 关闭线程池最佳实践
优雅地关闭线程池
线程池关闭时的资源清理
7.5 生产环境中的线程池监控
关键监控指标
监控实现
7.6 线程池适用场景与不适用场景
适用场景
不适用场景
- 线程池在框架中的应用
8.1 Spring框架中的线程池
TaskExecutor接口
Spring中的线程池配置
Spring的异步执行
Spring的定时任务调度
8.2 Tomcat中的线程池
Tomcat的线程模型
Tomcat线程池配置
Spring Boot中配置Tomcat线程池
Tomcat线程池的监控
8.3 Netty中的线程池
Netty的线程模型
自定义Netty线程池配置
使用业务线程池
8.4 MyBatis中的线程池
MyBatis的Executor
MyBatis-Spring与数据库连接池
8.5 RocketMQ中的线程池
RocketMQ的线程池架构
RocketMQ消费者线程池配置
自定义RocketMQ生产者的线程池
- 高级主题
9.1 ForkJoinPool与工作窃取算法
9.1.1 ForkJoinPool原理
9.1.2 ForkJoinPool vs ThreadPoolExecutor9.1.3 工作窃取算法
9.1.4 RecursiveTask和RecursiveAction
9.2 CompletableFuture与异步编程
9.2.1 CompletableFuture基础
9.2.2 组合多个CompletableFuture
9.2.3 CompletableFuture与线程池配合使用
9.3 Java 19虚拟线程与结构化并发
9.3.1 虚拟线程基础
9.3.2 虚拟线程与传统线程池的比较
9.3.3 结构化并发
9.3.4 虚拟线程最佳实践
9.4 高并发系统中的线程池优化
9.4.1 线程池隔离策略
9.4.2 动态线程池
9.4.3 断路器模式与线程池
9.4.4 队列背压与限流
- 总结与展望
10.1 核心知识回顾
10.2 线程池发展趋势
10.3 后续学习路径
1. 线程池基础
1.1 什么是线程池
线程池是一种线程使用模式。线程池维护多个线程,等待着监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程的开销,另一方面避免了线程数量膨胀导致的过分调度问题。
线程池可以看作是线程的集合,通过使用线程池,我们可以:
- 重用已创建的线程:避免因为线程创建和销毁所带来的性能开销
- 控制并发的线程数量:防止因为创建大量线程而导致系统资源耗尽
- 管理线程的生命周期:可以对线程进行统一的分配、调优和监控
1.2 为什么需要线程池
在并发编程中,多线程技术已经成为提高系统性能的重要手段。但直接创建线程存在以下问题:
- 频繁创建和销毁线程带来的系统开销大:线程的创建和销毁需要时间,如果任务执行时间很短,频繁创建销毁线程会使得性能下降
- 线程无限制创建,会导致系统资源耗尽:线程是有限的系统资源,如果无限制创建,可能会导致内存溢出
- 线程创建和管理缺乏统一的规划:不利于系统的维护和扩展
下面是一个对比示例,展示直接创建线程和使用线程池的区别:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo {
// 执行任务的数量
private static final int TASK_COUNT = 10000;
// 使用直接创建线程的方式执行任务
public static void executeWithDirectThreads() {
long start = System.currentTimeMillis();
for (int i = 0; i < TASK_COUNT; i++) {
Thread thread = new Thread(() -> {
// 模拟任务执行
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread.start();
}
long end = System.currentTimeMillis();
System.out.println("直接创建线程耗时: " + (end - start) + "ms");
}
// 使用线程池执行任务
public static void executeWithThreadPool() {
ExecutorService executor = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
for (int i = 0; i < TASK_COUNT; i++) {
executor.execute(() -> {
// 模拟任务执行
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
try {
// 等待所有任务完成
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long end = System.currentTimeMillis();
System.out.println("使用线程池耗时: " + (end - start) + "ms");
}
public static void main(String[] args) {
executeWithDirectThreads();
executeWithThreadPool();
}
}
运行上面的代码,可以明显看到使用线程池执行任务比直接创建线程要快得多,并且资源利用更加合理。
1.3 线程池的核心参数
Java中的线程池主要由ThreadPoolExecutor
类实现,它提供了一系列参数来配置线程池的行为:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
各参数详细介绍:
- corePoolSize(核心线程数):线程池中的基本线程数量,即使线程空闲,也会保持在池中,除非设置了
allowCoreThreadTimeOut
- maximumPoolSize(最大线程数):线程池允许创建的最大线程数
- keepAliveTime(线程存活时间):当线程数大于核心线程数时,多余的空闲线程在终止前等待新任务的最长时间
- unit(时间单位):
keepAliveTime
参数的时间单位,可以是毫秒、秒、分钟等 - workQueue(工作队列):用于保存等待执行的任务的阻塞队列
- threadFactory(线程工厂):用于创建新线程的工厂
- handler(拒绝策略):当队列和线程池都满了,新任务的处理策略
1.4 线程池工作原理
线程池的工作原理如下图所示:
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ │ │ │
│ 任务提交 │ │ 任务执行 │
│ │ │ │
│ │ │ │
└─────────┬───────────┘ └─────────▲───────────┘
│ │
│ │
│ │
▼ │
┌─────────────────────┐ ┌────────┴────────┐
│ │ yes │ │
│ 核心线程池是否已满? ├──────►│ 创建新的线程 │
│ │ │ │
└─────────┬───────────┘ └─────────────────┘
│ no
│
▼
┌─────────────────────┐ ┌─────────────────────┐
│ │ yes │ │
│ 工作队列是否已满? ├──────►│最大线程池是否已满? │
│ │ │ │
└─────────┬───────────┘ └─────────┬───────────┘
│ no │
│ │ no
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ 将任务加入工作队列 │ │ 创建新的线程 │
│ │ │ │
└─────────────────────┘ └─────────────────────┘
│
│ yes
▼
┌─────────────────────┐
│ │
│ 执行拒绝策略 │
│ │
└─────────────────────┘
线程池的处理流程:
- 当提交一个新任务到线程池时,线程池会先检查核心线程池是否已满
- 如果核心线程池未满,则创建一个新的工作线程来执行任务
- 如果核心线程池已满,则将任务添加到工作队列中
- 如果工作队列已满,则线程池会检查最大线程池是否已满
- 如果最大线程池未满,则创建一个新的工作线程来执行任务
- 如果最大线程池已满,则根据拒绝策略来处理该任务
下面通过一个简单的示例来演示线程池的处理流程:
import java.util.concurrent.*;
public class ThreadPoolWorkingDemo {
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60, TimeUnit.SECONDS, // 线程存活时间
new ArrayBlockingQueue<>(2), // 工作队列容量为2
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 使用调用者运行策略
);
// 模拟10个任务提交到线程池
for (int i = 1; i <= 10; i++) {
final int taskId = i;
System.out.println("提交任务: " + taskId);
executor.execute(() -> {
System.out.println("开始执行任务: " + taskId +
", 线程名: " + Thread.currentThread().getName());
try {
// 模拟任务执行耗时
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("完成任务: " + taskId +
", 线程名: " + Thread.currentThread().getName());
});
System.out.println("线程池状态: " +
"核心线程数: " + executor.getCorePoolSize() +
", 活动线程数: " + executor.getActiveCount() +
", 最大线程数: " + executor.getMaximumPoolSize() +
", 线程池大小: " + executor.getPoolSize() +
", 队列任务数: " + executor.getQueue().size() +
", 已完成任务数: " + executor.getCompletedTaskCount());
}
// 关闭线程池
executor.shutdown();
}
}
2. Java中的线程池类型与使用
2.1 常见的线程池类型
Java通过Executors
工厂类提供了几种常见的线程池实现,每种线程池都有其特定的使用场景:
2.1.1 FixedThreadPool
FixedThreadPool
是一个固定大小的线程池,它的特点是:
- 核心线程数和最大线程数相同
- 使用无界队列
LinkedBlockingQueue
- 线程会一直存活,不会被回收
- 适合处理长期存在的任务,负载较重的服务器
// 创建固定大小为5的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
内部实现:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, // 核心线程数
nThreads, // 最大线程数(与核心线程数相同)
0L, // 线程存活时间(不会回收)
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() // 无界队列
);
}
2.1.2 CachedThreadPool
CachedThreadPool
是一个会根据需要创建新线程的线程池,它的特点是:
- 核心线程数为0,最大线程数为Integer.MAX_VALUE
- 使用
SynchronousQueue
作为工作队列 - 空闲线程会在60秒后被回收
- 适合执行大量短期异步任务的程序
// 创建缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
内部实现:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0, // 核心线程数为0
Integer.MAX_VALUE, // 最大线程数非常大
60L, // 空闲线程存活时间
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>() // 同步队列
);
}
2.1.3 SingleThreadExecutor
SingleThreadExecutor
是一个只有一个工作线程的线程池,它的特点是:
- 核心线程数和最大线程数都为1
- 使用无界队列
LinkedBlockingQueue
- 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
- 适合需要保证顺序执行各个任务的应用场景
// 创建单线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
内部实现:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(
1, // 核心线程数为1
1, // 最大线程数也为1
0L, // 线程存活时间(不会回收)
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() // 无界队列
));
}
2.1.4 ScheduledThreadPool
ScheduledThreadPool
是一个可以定时执行任务的线程池,它的特点是:
- 核心线程数固定,最大线程数为Integer.MAX_VALUE
- 使用
DelayedWorkQueue
作为工作队列 - 可以执行定时任务或周期性任务
- 适合需要多个后台线程执行周期任务的场景
// 创建具有3个核心线程的ScheduledThreadPool
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
内部实现:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor的构造函数
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(
corePoolSize, // 指定核心线程数
Integer.MAX_VALUE, // 最大线程数非常大
0, // 线程存活时间
TimeUnit.NANOSECONDS,
new DelayedWorkQueue() // 延迟工作队列
);
}
2.2 线程池的使用示例
2.2.1 FixedThreadPool的使用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class FixedThreadPoolExample {
public static void main(String[] args) {
// 创建固定大小为3的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交10个任务给线程池执行
for (int i = 1; i <= 10; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("Task " + taskId + " is running on " +
Thread.currentThread().getName());
try {
// 模拟任务执行
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Task " + taskId + " completed");
});
}
// 关闭线程池
executor.shutdown();
// 等待所有任务完成
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("All tasks completed");
}
}
2.2.2 CachedThreadPool的使用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class CachedThreadPoolExample {
public static void main(String[] args) {
// 创建缓存线程池
ExecutorService executor = Executors.newCachedThreadPool();
// 提交20个短时任务给线程池执行
for (int i = 1; i <= 20; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("Task " + taskId + " is running on " +
Thread.currentThread().getName());
try {
// 模拟短时任务执行
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Task " + taskId + " completed");
});
// 任务提交间隔100毫秒
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 关闭线程池
executor.shutdown();
// 等待所有任务完成
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("All tasks completed");
}
}
2.2.3 SingleThreadExecutor的使用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
// 创建单线程的线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交5个任务给线程池执行
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("Task " + taskId + " is running on " +
Thread.currentThread().getName());
try {
// 模拟任务执行
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Task " + taskId + " completed");
});
}
// 关闭线程池
executor.shutdown();
// 等待所有任务完成
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("All tasks completed in order");
}
}
2.2.4 ScheduledThreadPool的使用
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExample {
public static void main(String[] args) {
// 创建具有2个核心线程的ScheduledThreadPool
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 1. 延迟执行任务
scheduler.schedule(() -> {
System.out.println("Delayed task executed after 2 seconds");
}, 2, TimeUnit.SECONDS);
// 2. 固定速率执行任务(每3秒执行一次)
scheduler.scheduleAtFixedRate(() -> {
System.out.println("Fixed rate task executed at " +
System.currentTimeMillis() / 1000);
try {
// 模拟任务耗时
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, 0, 3, TimeUnit.SECONDS);
// 3. 固定延迟执行任务(上一次任务执行完毕后延迟2秒再执行)
scheduler.scheduleWithFixedDelay(() -> {
System.out.println("Fixed delay task executed at " +
System.currentTimeMillis() / 1000);
try {
// 模拟任务耗时
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, 0, 2, TimeUnit.SECONDS);
// 让程序运行15秒后关闭调度器
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 关闭调度器
scheduler.shutdown();
System.out.println("Scheduler shut down");
}
}
2.3 线程池的优缺点
2.3.1 线程池的优点
- 降低资源消耗:通过重用已创建的线程,降低线程创建和销毁造成的消耗
- 提高响应速度:任务到达时,无需等待线程创建即可立即执行
- 提高线程的可管理性:线程是稀缺资源,如果无限制创建,会消耗系统资源,降低系统稳定性
- 提供更多更强大的功能:如延时、定时线程池,可以满足不同业务场景需求
2.3.2 线程池的缺点
- 线程池配置不当会引发风险:如果核心线程数设置过大,会占用过多系统资源
- 使用无界队列可能导致OOM:如
FixedThreadPool
和SingleThreadExecutor
默认使用无界队列,可能导致内存溢出 - 线程池运行和关闭处理不当可能造成任务丢失:非正确的关闭线程池可能导致任务未被执行
- Executors工厂类创建的池不够灵活:预定义的线程池未必满足所有业务场景需求
3. ThreadPoolExecutor详解
3.1 ThreadPoolExecutor的生命周期
ThreadPoolExecutor
有五种状态,分别是:
- RUNNING: 接受新任务并处理队列中的任务
- SHUTDOWN: 不接受新任务,但处理队列中的任务
- STOP: 不接受新任务,不处理队列中的任务,中断正在执行的任务
- TIDYING: 所有任务已终止,工作线程数为0,准备运行terminated()钩子方法
- TERMINATED: terminated()方法已执行完成
状态转换图如下:
RUNNING -> SHUTDOWN: 调用shutdown()方法
| |
| ▼
| TIDYING: 队列和线程池为空
| |
| ▼
| TERMINATED: terminated()方法执行完毕
|
▼
STOP: 调用shutdownNow()方法
|
▼
TIDYING: 线程池为空
|
▼
TERMINATED: terminated()方法执行完毕
下面是一个演示线程池生命周期的示例代码:
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolLifecycleDemo {
public static void main(String[] args) throws InterruptedException {
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadFactory() {
private final AtomicInteger counter = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "MyThread-" + counter.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 打印初始状态
printThreadPoolStatus("初始状态", executor);
// 提交任务
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("执行任务" + taskId + ", 线程: " +
Thread.currentThread().getName());
try {
Thread.sleep(2000); // 任务执行耗时2秒
} catch (InterruptedException e) {
System.out.println("任务" + taskId + "被中断");
Thread.currentThread().interrupt();
}
return "任务" + taskId + "结果";
});
}
// 打印提交任务后的状态
Thread.sleep(1000);
printThreadPoolStatus("提交任务后", executor);
// 调用shutdown()
System.out.println("\n调用shutdown()...");
executor.shutdown();
printThreadPoolStatus("调用shutdown()后", executor);
// 等待一段时间让任务完成
Thread.sleep(3000);
printThreadPoolStatus("执行任务后", executor);
// 创建新的线程池演示shutdownNow()
ThreadPoolExecutor executor2 = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadFactory() {
private final AtomicInteger counter = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "MyThread2-" + counter.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 提交任务到新线程池
for (int i = 6; i <= 10; i++) {
final int taskId = i;
executor2.submit(() -> {
System.out.println("执行任务" + taskId + ", 线程: " +
Thread.currentThread().getName());
try {
Thread.sleep(10000); // 任务执行耗时10秒
} catch (InterruptedException e) {
System.out.println("任务" + taskId + "被中断");
Thread.currentThread().interrupt();
}
return "任务" + taskId + "结果";
});
}
// 等待确保任务开始执行
Thread.sleep(1000);
printThreadPoolStatus("executor2提交任务后", executor2);
// 调用shutdownNow()
System.out.println("\n调用shutdownNow()...");
executor2.shutdownNow();
printThreadPoolStatus("调用shutdownNow()后", executor2);
// 等待观察状态变化
Thread.sleep(2000);
printThreadPoolStatus("等待2秒后", executor2);
}
// 打印线程池状态的辅助方法
private static void printThreadPoolStatus(String prefix, ThreadPoolExecutor executor) {
System.out.println("\n" + prefix + ":");
System.out.println("线程池大小: " + executor.getPoolSize());
System.out.println("活动线程数: " + executor.getActiveCount());
System.out.println("完成任务数: " + executor.getCompletedTaskCount());
System.out.println("队列任务数: " + executor.getQueue().size());
System.out.println("线程池是否已关闭: " + executor.isShutdown());
System.out.println("线程池是否正在终止: " + executor.isTerminating());
System.out.println("线程池是否已终止: " + executor.isTerminated());
}
}
3.2 ThreadPoolExecutor执行流程
ThreadPoolExecutor执行任务的核心流程如下:
- 如果当前运行的线程数小于核心线程数,则创建新线程来执行任务。
- 如果当前运行的线程数大于或等于核心线程数,则将任务加入工作队列。
- 如果工作队列已满,且当前运行的线程数小于最大线程数,则创建新线程来执行任务。
- 如果工作队列已满,且当前运行的线程数大于或等于最大线程数,则根据拒绝策略来处理该任务。
下面是ThreadPoolExecutor
中的核心方法execute
的简化实现逻辑:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 步骤1:当前线程数小于核心线程数,创建新线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 步骤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);
}
// 步骤3和4:尝试创建新线程,如果失败则拒绝
else if (!addWorker(command, false))
reject(command);
}
下面是一个完整的例子,演示了不同配置下线程池的行为:
import java.util.concurrent.*;
public class ThreadPoolExecutorFlowDemo {
public static void main(String[] args) throws InterruptedException {
// 展示不同配置下线程池的行为
// 案例1: 核心线程数=2, 最大线程数=4, 队列容量=2
System.out.println("=== 案例1: 核心线程数=2, 最大线程数=4, 队列容量=2 ===");
// 足够创建2个核心线程和2个非核心线程,队列可存放2个任务
testThreadPoolBehavior(2, 4, 2, 10);
Thread.sleep(1000);
// 案例2: 核心线程数=4, 最大线程数=4, 队列容量=10
System.out.println("\n=== 案例2: 核心线程数=4, 最大线程数=4, 队列容量=10 ===");
// 任务数量不足以填满队列,都会创建核心线程执行
testThreadPoolBehavior(4, 4, 10, 6);
Thread.sleep(1000);
// 案例3: 核心线程数=2, 最大线程数=3, 队列容量=1
System.out.println("\n=== 案例3: 核心线程数=2, 最大线程数=3, 队列容量=1 ===");
// 部分任务会被拒绝
testThreadPoolBehavior(2, 3, 1, 10);
}
private static void testThreadPoolBehavior(
int corePoolSize, int maximumPoolSize, int queueCapacity, int taskCount)
throws InterruptedException {
// 创建一个有界队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(queueCapacity);
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
60, TimeUnit.SECONDS,
workQueue,
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
System.out.println("创建新线程: " + t.getName());
return t;
}
},
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略: 抛出异常
);
// 跟踪被拒绝的任务
int rejectedTasks = 0;
// 提交任务
for (int i = 1; i <= taskCount; i++) {
final int taskId = i;
try {
executor.execute(() -> {
System.out.println("执行任务" + taskId + ",线程: " +
Thread.currentThread().getName());
try {
Thread.sleep(2000); // 任务执行耗时2秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("完成任务" + taskId);
});
System.out.println("提交任务" + taskId + " - " +
"线程池大小: " + executor.getPoolSize() + ", " +
"活动线程数: " + executor.getActiveCount() + ", " +
"队列中任务数: " + executor.getQueue().size());
} catch (RejectedExecutionException e) {
System.out.println("任务" + taskId + "被拒绝");
rejectedTasks++;
}
}
System.out.println("\n任务提交完毕,被拒绝任务数: " + rejectedTasks);
System.out.println("核心线程数: " + executor.getCorePoolSize());
System.out.println("最大线程数: " + executor.getMaximumPoolSize());
System.out.println("线程池大小: " + executor.getPoolSize());
System.out.println("活动线程数: " + executor.getActiveCount());
System.out.println("队列中任务数: " + executor.getQueue().size());
// 优雅关闭线程池
executor.shutdown();
}
}
3.3 线程池参数调优
为了使线程池发挥最佳性能,我们需要根据应用程序的特点和系统资源合理配置线程池参数。
3.3.1 核心线程数和最大线程数
线程数的设置主要取决于任务的类型和CPU核心数:
-
CPU密集型任务:线程数 = CPU核心数 + 1
- 主要是执行计算任务,不涉及IO操作,线程数略多于CPU核心数可以充分利用CPU资源
-
IO密集型任务:线程数 = CPU核心数 * (1 + IO耗时/CPU耗时)
- 或者简便公式:线程数 = CPU核心数 * 2
- 大部分时间在等待IO操作完成,此时CPU是空闲的,因此可以配置更多线程
// 获取系统可用处理器数量
int availableProcessors = Runtime.getRuntime().availableProcessors();
// CPU密集型任务的线程数
int cpuIntensiveThreads = availableProcessors + 1;
// IO密集型任务的线程数
int ioIntensiveThreads = availableProcessors * 2;
// 混合型任务可以取中间值
int mixedThreads = (cpuIntensiveThreads + ioIntensiveThreads) / 2;
3.3.2 工作队列的选择
选择合适的工作队列对线程池性能有重要影响:
- LinkedBlockingQueue:无界队列,容量可增长,适合任务量可预知且相对稳定的场景
- ArrayBlockingQueue:有界队列,容量固定,适合控制资源使用量的场景
- SynchronousQueue:不存储任务,适合任务处理快速的场景
- PriorityBlockingQueue:优先级队列,适合任务优先级区分明显的场景
- DelayQueue:延时队列,适合延时执行任务的场景
3.3.3 拒绝策略的选择
根据业务需求选择合适的拒绝策略:
- AbortPolicy:直接抛出异常,适合快速失败的场景
- CallerRunsPolicy:调用者线程执行任务,适合任务必须执行且可接受延迟的场景
- DiscardPolicy:丢弃任务,不通知,适合任务可丢弃的场景
- DiscardOldestPolicy:丢弃最旧的任务,适合新任务优先级高的场景
- 自定义拒绝策略:根据业务需求自定义处理方式
一个综合的参数调优示例:
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolTuningExample {
public static void main(String[] args) {
// 确定系统CPU核心数
int cpuCores = Runtime.getRuntime().availableProcessors();
System.out.println("系统CPU核心数: " + cpuCores);
// 计算合适的线程数
int taskType = determineTaskType(); // 返回0表示CPU密集型,1表示IO密集型,2表示混合型
int threadCount;
if (taskType == 0) {
threadCount = cpuCores + 1;
System.out.println("CPU密集型任务,线程数设置为: " + threadCount);
} else if (taskType == 1) {
threadCount = cpuCores * 2;
System.out.println("IO密集型任务,线程数设置为: " + threadCount);
} else {
threadCount = (cpuCores + 1 + cpuCores * 2) / 2;
System.out.println("混合型任务,线程数设置为: " + threadCount);
}
// 创建自定义的线程工厂
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger threadCounter = new AtomicInteger(1);
private final String namePrefix = "AppWorker-";
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + threadCounter.getAndIncrement());
// 设置为非守护线程
t.setDaemon(false);
// 设置默认优先级
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
};
// 选择合适的工作队列
BlockingQueue<Runnable> workQueue;
int queueType = determineQueueType(); // 返回队列类型
if (queueType == 0) {
// 固定大小的有界队列
workQueue = new ArrayBlockingQueue<>(1000);
System.out.println("使用ArrayBlockingQueue,容量: 1000");
} else if (queueType == 1) {
// 无界队列
workQueue = new LinkedBlockingQueue<>();
System.out.println("使用LinkedBlockingQueue,无界队列");
} else if (queueType == 2) {
// 同步队列,直接交付
workQueue = new SynchronousQueue<>();
System.out.println("使用SynchronousQueue,直接交付任务");
} else {
// 优先级队列
workQueue = new PriorityBlockingQueue<>();
System.out.println("使用PriorityBlockingQueue,按优先级执行任务");
}
// 选择合适的拒绝策略
RejectedExecutionHandler rejectionHandler;
int rejectionType = determineRejectionType(); // 返回拒绝策略类型
if (rejectionType == 0) {
// 直接抛出异常
rejectionHandler = new ThreadPoolExecutor.AbortPolicy();
System.out.println("使用AbortPolicy拒绝策略");
} else if (rejectionType == 1) {
// 调用者运行
rejectionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
System.out.println("使用CallerRunsPolicy拒绝策略");
} else if (rejectionType == 2) {
// 丢弃任务
rejectionHandler = new ThreadPoolExecutor.DiscardPolicy();
System.out.println("使用DiscardPolicy拒绝策略");
} else {
// 丢弃最旧的任务
rejectionHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
System.out.println("使用DiscardOldestPolicy拒绝策略");
}
// 配置线程池参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
threadCount, // 核心线程数
threadCount, // 最大线程数(与核心线程数相同,避免线程数波动)
60, TimeUnit.SECONDS, // 空闲线程存活时间
workQueue, // 工作队列
threadFactory, // 线程工厂
rejectionHandler // 拒绝策略
);
// 根据需要预热线程池
if (shouldPrestartThreads()) {
System.out.println("预热线程池,创建所有核心线程");
executor.prestartAllCoreThreads();
}
// 配置是否允许核心线程超时
boolean allowCoreThreadTimeout = shouldAllowCoreThreadTimeout();
executor.allowCoreThreadTimeOut(allowCoreThreadTimeout);
System.out.println("核心线程超时配置: " + (allowCoreThreadTimeout ? "允许" : "不允许"));
// 测试配置的线程池
testThreadPool(executor);
// 关闭线程池
executor.shutdown();
}
// 根据任务特性确定任务类型
private static int determineTaskType() {
// 这里简化处理,实际应用中应该根据应用特性和性能测试结果来确定
// 0: CPU密集型, 1: IO密集型, 2: 混合型
return 1; // 假设是IO密集型任务
}
// 确定使用的队列类型
private static int determineQueueType() {
// 0: ArrayBlockingQueue, 1: LinkedBlockingQueue,
// 2: SynchronousQueue, 3: PriorityBlockingQueue
return 0; // 假设使用ArrayBlockingQueue
}
// 确定使用的拒绝策略
private static int determineRejectionType() {
// 0: AbortPolicy, 1: CallerRunsPolicy,
// 2: DiscardPolicy, 3: DiscardOldestPolicy
return 1; // 假设使用CallerRunsPolicy
}
// 是否应该预启动所有核心线程
private static boolean shouldPrestartThreads() {
// 根据应用特性决定是否预启动线程
return true; // 假设需要预启动
}
// 是否允许核心线程超时
private static boolean shouldAllowCoreThreadTimeout() {
// 根据应用特性决定是否允许核心线程超时
return false; // 假设不允许核心线程超时
}
// 测试线程池
private static void testThreadPool(ThreadPoolExecutor executor) {
System.out.println("\n开始测试线程池...");
// 提交一些任务
for (int i = 1; i <= 20; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("任务" + taskId + "开始执行,线程: " +
Thread.currentThread().getName());
// 模拟任务执行
try {
// 随机执行时间,模拟不同负载
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任务" + taskId + "执行完成");
return taskId;
});
}
// 监控线程池状态
monitorThreadPool(executor);
}
// 监控线程池状态
private static void monitorThreadPool(ThreadPoolExecutor executor) {
try {
// 等待一段时间让任务开始执行
Thread.sleep(500);
// 打印线程池状态
System.out.println("\n===== 线程池状态 =====");
System.out.println("活动线程数: " + executor.getActiveCount());
System.out.println("线程池大小: " + executor.getPoolSize());
System.out.println("队列中等待任务数: " + executor.getQueue().size());
System.out.println("已完成任务数: " + executor.getCompletedTaskCount());
// 等待大部分任务完成
Thread.sleep(3000);
// 再次打印状态
System.out.println("\n===== 执行后线程池状态 =====");
System.out.println("活动线程数: " + executor.getActiveCount());
System.out.println("线程池大小: " + executor.getPoolSize());
System.out.println("队列中等待任务数: " + executor.getQueue().size());
System.out.println("已完成任务数: " + executor.getCompletedTaskCount());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4. 阻塞队列与线程池的关系
在线程池实现中,阻塞队列(BlockingQueue)是一个核心组件,用于存储等待执行的任务。了解不同类型的阻塞队列及其特性,对于合理配置线程池至关重要。
4.1 常用阻塞队列类型
Java中提供了多种BlockingQueue实现,每种实现都有其特点和适用场景:
4.1.1 ArrayBlockingQueue
有界阻塞队列,基于数组实现的FIFO(先进先出)队列。
// 创建一个容量为100的ArrayBlockingQueue
BlockingQueue<Runnable> arrayQueue = new ArrayBlockingQueue<>(100);
// 在线程池中使用
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60, TimeUnit.SECONDS, arrayQueue);
特点:
- 容量固定,创建时必须指定大小
- 公平性可选(可以保证FIFO的顺序)
- 内部使用一把锁,同一时刻只能有一个线程入队或出队
- 适用于消费者和生产者速度差不多,要求严格控制队列长度的场景
4.1.2 LinkedBlockingQueue
基于链表实现的可选有界阻塞队列,默认为无界队列。
// 创建一个无界LinkedBlockingQueue
BlockingQueue<Runnable> unboundedQueue = new LinkedBlockingQueue<>();
// 创建一个有界LinkedBlockingQueue
BlockingQueue<Runnable> boundedQueue = new LinkedBlockingQueue<>(200);
// 在线程池中使用
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60, TimeUnit.SECONDS, boundedQueue);
特点:
- 默认容量为Integer.MAX_VALUE,相当于无界队列
- 可选择创建有界队列
- 入队和出队操作使用不同的锁,可以并行执行
- 适用于消费者速度慢于生产者,但不希望拒绝任务的场景
注意:使用无界队列时,当任务生产速度持续快于消费速度,可能导致队列中的任务无限增长,最终导致内存溢出(OOM)。
4.1.3 SynchronousQueue
一个不存储元素的阻塞队列,每个插入操作必须等待另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
// 创建一个SynchronousQueue
BlockingQueue<Runnable> synchronousQueue = new SynchronousQueue<>();
// 在线程池中使用
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, synchronousQueue);
特点:
- 没有容量,不存储任务
- 每个插入操作必须等待对应的移除操作
- 可选公平性
- 适用于任务处理快速,不需要队列缓冲的场景
- 通常与无限大小的maximumPoolSize一起使用,确保可以创建足够的线程处理任务
4.1.4 PriorityBlockingQueue
一个支持优先级的无界阻塞队列。
// 创建一个自定义比较器的PriorityBlockingQueue
BlockingQueue<Runnable> priorityQueue = new PriorityBlockingQueue<>(
11, (o1, o2) -> {
// 假设Runnable是PriorityTask的实例
PriorityTask task1 = (PriorityTask) o1;
PriorityTask task2 = (PriorityTask) o2;
return task1.getPriority() - task2.getPriority();
});
// 在线程池中使用
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60, TimeUnit.SECONDS, priorityQueue);
特点:
- 默认容量为11,但可增长至Integer.MAX_VALUE
- 元素按优先级出队,由Comparator或自然顺序决定
- 适用于有任务优先级要求的场景
4.1.5 DelayQueue
一个支持延迟获取元素的无界阻塞队列。
// 创建延迟任务类
class DelayedTask implements Delayed {
private final String name;
private final long delayInMillis;
private final long startTime;
public DelayedTask(String name, long delayInMillis) {
this.name = name;
this.delayInMillis = delayInMillis;
this.startTime = System.currentTimeMillis();
}
@Override
public long getDelay(TimeUnit unit) {
long elapsedTime = System.currentTimeMillis() - startTime;
return unit.convert(delayInMillis - elapsedTime, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.getDelay(TimeUnit.MILLISECONDS),
o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return "DelayedTask [name=" + name + ", delay=" + delayInMillis + "ms]";
}
public void execute() {
System.out.println("执行任务: " + name);
}
}
// 创建DelayQueue
DelayQueue<DelayedTask> delayQueue = new DelayQueue<>();
// 在自定义线程池中使用
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>()) {
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
// 从DelayQueue中获取到期的任务
DelayedTask task = null;
try {
task = delayQueue.take(); // 阻塞直到有任务到期
task.execute();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
特点:
- 队列中的元素只有到了指定的延迟时间才能被取出
- 队列头部是延迟时间最短的元素
- 适用于定时任务调度
4.2 阻塞队列对线程池行为的影响
不同的阻塞队列类型会导致线程池表现出不同的行为:
4.2.1 有界队列
当使用ArrayBlockingQueue等有界队列时:
- 可以控制资源的使用量,防止因任务堆积导致内存溢出
- 当队列满时,线程池会创建新线程直到maximumPoolSize
- 如果线程数达到maximumPoolSize且队列已满,新任务会被拒绝
- 适合需要限制任务处理数量的场景
// 有界队列示例
ArrayBlockingQueue<Runnable> boundedQueue = new ArrayBlockingQueue<>(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60, TimeUnit.SECONDS, boundedQueue);
4.2.2 无界队列
当使用默认的LinkedBlockingQueue等无界队列时:
- maximumPoolSize参数无效,因为队列永远不会满
- 线程池的线程数最多为corePoolSize
- 任务可能在队列中无限堆积,可能导致内存溢出
- 适合任务处理速度稳定,不会长时间积压的场景
// 无界队列示例
LinkedBlockingQueue<Runnable> unboundedQueue = new LinkedBlockingQueue<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60, TimeUnit.SECONDS, unboundedQueue);
// 注意:maximumPoolSize为10,但实际上不会超过5(corePoolSize)
4.2.3 同步队列
当使用SynchronousQueue时:
- 任务不会在队列中排队,必须立即由线程处理
- 如果无法立即处理,会尝试创建新线程
- 如果线程数达到maximumPoolSize,新任务会被拒绝
- 适合需要快速执行且可接受拒绝的场景
- 通常需要较大的maximumPoolSize值
// 同步队列示例
SynchronousQueue<Runnable> syncQueue = new SynchronousQueue<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 100, 60, TimeUnit.SECONDS, syncQueue);
4.3 队列选择指南
根据不同的应用场景选择合适的队列类型:
-
短小且快速处理的任务:使用SynchronousQueue,搭配较大的maximumPoolSize
new ThreadPoolExecutor(10, 200, 60, TimeUnit.SECONDS, new SynchronousQueue<>());
-
执行时间长但值得等待的任务:使用有界ArrayBlockingQueue,控制并发量
new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
-
需要按优先级处理的任务:使用PriorityBlockingQueue
new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new PriorityBlockingQueue<>());
-
需要按照延迟时间执行的任务:使用DelayQueue或结合ScheduledThreadPoolExecutor
// 使用ScheduledThreadPoolExecutor更适合定时任务 ScheduledThreadPoolExecutor scheduledExecutor = new ScheduledThreadPoolExecutor(5);
-
生产速度远大于消费速度的任务:慎用无界队列,考虑使用有界LinkedBlockingQueue
new ThreadPoolExecutor(5, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));
4.4 阻塞队列性能对比
不同阻塞队列在吞吐量、延迟和内存使用方面表现不同:
队列类型 | 吞吐量 | 延迟 | 内存使用 | 并发级别 |
---|---|---|---|---|
ArrayBlockingQueue | 中等 | 中等 | 低 | 低(一把锁) |
LinkedBlockingQueue | 高 | 中等 | 高 | 中(两把锁) |
SynchronousQueue | 低 | 低 | 极低 | 高 |
PriorityBlockingQueue | 低 | 高 | 中 | 低 |
DelayQueue | 低 | 高 | 中 | 低 |
下面是一个简单的性能测试示例:
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class QueuePerformanceTest {
public static void main(String[] args) throws InterruptedException {
// 测试不同队列类型
testQueue(new ArrayBlockingQueue<>(10000), "ArrayBlockingQueue");
testQueue(new LinkedBlockingQueue<>(10000), "LinkedBlockingQueue");
testQueue(new SynchronousQueue<>(), "SynchronousQueue");
testQueue(new PriorityBlockingQueue<>(10000), "PriorityBlockingQueue");
}
private static void testQueue(BlockingQueue<Integer> queue, String queueName)
throws InterruptedException {
System.out.println("测试: " + queueName);
final int PRODUCER_COUNT = 4;
final int CONSUMER_COUNT = 4;
final int TASK_COUNT = 100000;
// 计数
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch endLatch = new CountDownLatch(TASK_COUNT);
AtomicLong totalTime = new AtomicLong(0);
// 启动消费者线程
for (int i = 0; i < CONSUMER_COUNT; i++) {
new Thread(() -> {
try {
startLatch.await();
while (true) {
Integer value = queue.poll(100, TimeUnit.MILLISECONDS);
if (value != null) {
// 减少计数
endLatch.countDown();
if (endLatch.getCount() == 0) {
break;
}
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Consumer-" + i).start();
}
// 启动生产者线程
for (int i = 0; i < PRODUCER_COUNT; i++) {
final int producerId = i;
new Thread(() -> {
try {
startLatch.await();
int start = producerId * (TASK_COUNT / PRODUCER_COUNT);
int end = (producerId + 1) * (TASK_COUNT / PRODUCER_COUNT);
for (int j = start; j < end; j++) {
long startTime = System.nanoTime();
queue.put(j);
long endTime = System.nanoTime();
totalTime.addAndGet(endTime - startTime);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Producer-" + i).start();
}
// 开始测试
long testStartTime = System.currentTimeMillis();
startLatch.countDown();
// 等待所有任务完成
boolean completed = endLatch.await(60, TimeUnit.SECONDS);
long testEndTime = System.currentTimeMillis();
if (completed) {
System.out.println("总耗时: " + (testEndTime - testStartTime) + "ms");
System.out.println("平均入队时间: " +
(totalTime.get() / TASK_COUNT) + "ns");
System.out.println("吞吐量: " +
(TASK_COUNT * 1000 / (testEndTime - testStartTime)) +
" 操作/秒");
} else {
System.out.println("测试超时,可能是队列特性导致的。");
}
System.out.println("------------------------------");
// 等待一会儿让线程池资源释放
Thread.sleep(1000);
}
}