一、Executors框架概述
在Java并发编程中,Executors是一个工具类,位于java.util.concurrent包下,它提供了创建线程池的静态工厂方法。合理使用线程池可以有效控制系统中并发线程的数量,避免因频繁创建和销毁线程带来的性能开销。
Executors框架的核心优势
-
资源复用:避免频繁创建和销毁线程的开销
-
统一管理:集中管理线程的生命周期和任务分配
-
提升响应速度:任务到达时无需等待线程创建即可执行
二、三大核心线程池详解
1. newCachedThreadPool ------ 可缓存线程池
特点说明
-
线程池长度超过处理需要时,可灵活回收空闲线程
-
若无可回收线程,则新建线程
-
适用于执行大量短期异步任务的场景
核心特性
| 特性 | 说明 |
|---|---|
| 核心线程数 | 0 |
| 最大线程数 | Integer.MAX_VALUE |
| 空闲线程存活时间 | 60秒 |
| 工作队列 | SynchronousQueue |
代码示例
java
public class ThreadPoolTest2 {
/**
* newCachedThreadPool 创建一个可缓存线程池,
* 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
*/
public static void test_2() {
// 创建可缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " 执行任务: " + index
+ " 当前时间: " + new Date().getSeconds());
}
});
}
// 注意:这里需要关闭线程池,否则程序不会结束
cachedThreadPool.shutdown();
}
public static void main(String[] args) {
test_2();
}
}
运行特点
-
线程会被复用,控制台输出中可能看到同一个线程名执行了多个任务
-
空闲60秒后线程会自动回收
-
适合处理生命周期短、执行频率高的任务
2. newSingleThreadExecutor ------ 单线程化线程池
特点说明
-
只会用唯一的工作线程来执行所有任务
-
保证所有任务按照指定顺序执行
-
支持FIFO(先进先出)、LIFO(后进先出)、优先级等排序方式
核心特性
| 特性 | 说明 |
|---|---|
| 核心线程数 | 1 |
| 最大线程数 | 1 |
| 空闲线程存活时间 | 0秒 |
| 工作队列 | LinkedBlockingQueue |
代码示例
java
public class ThreadPoolTest3 {
/**
* newSingleThreadExecutor 创建一个单线程化的线程池,
* 它只会用唯一的工作线程来执行任务,
* 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
*/
public static void test_4() {
// 创建单线程线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
// 会按顺序打印 1,2,3,4,5,6,7,8,9,10
System.out.println(Thread.currentThread().getName() + " : " + index);
// 模拟任务处理耗时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
singleThreadExecutor.shutdown();
}
public static void main(String[] args) {
test_4();
}
}
运行特点
-
所有任务都使用同一个线程执行
-
任务严格按照提交顺序执行(FIFO)
-
即使某个任务执行失败,线程池也会创建一个新线程继续执行后续任务
-
适合需要保证顺序执行、不需要多线程并发的场景
3. newFixedThreadPool ------ 定长线程池
特点说明
-
创建固定长度的线程池
-
可控制线程最大并发数
-
超出的线程会在队列中等待
-
当线程池数量为1时,类似于单线程化线程池的效果
核心特性
| 特性 | 说明 |
|---|---|
| 核心线程数 | nThreads(参数指定) |
| 最大线程数 | nThreads(参数指定) |
| 空闲线程存活时间 | 0秒 |
| 工作队列 | LinkedBlockingQueue |
代码示例
java
public class ThreadPoolTest4 {
/**
* newFixedThreadPool 创建一个定长线程池,
* 可控制线程最大并发数,超出的线程会在队列中等待
*/
public static void test_3() {
// 创建固定大小为2的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()
+ " 正在执行任务: " + index);
Thread.sleep(500); // 模拟任务执行
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
fixedThreadPool.shutdown();
}
/**
* 演示当参数为1时的顺序执行效果
* 此时类似于newSingleThreadExecutor
*/
public static void test_order() {
// 当参数为1时,可以控制线程的执行顺序,类似join的作用
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
for (int i = 0; i < 5; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("顺序执行: " + index);
}
});
}
fixedThreadPool.shutdown();
}
public static void main(String[] args) {
System.out.println("=== 固定2个线程的线程池 ===");
test_3();
// 等待上面的任务执行完毕
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\n=== 固定1个线程的线程池(顺序执行) ===");
test_order();
}
}
运行特点
-
线程池中的线程数量保持恒定
-
当所有线程都在处理任务时,新任务会在队列中等待
-
设置大小为1时,效果等同于
newSingleThreadExecutor -
适合执行长期任务的场景
三、三种线程池对比
| 特性 | newCachedThreadPool | newSingleThreadExecutor | newFixedThreadPool |
|---|---|---|---|
| 核心线程数 | 0 | 1 | nThreads |
| 最大线程数 | Integer.MAX_VALUE | 1 | nThreads |
| 队列类型 | SynchronousQueue | LinkedBlockingQueue | LinkedBlockingQueue |
| 线程复用方式 | 动态创建和回收 | 固定单线程 | 固定数量线程 |
| 适用场景 | 短期异步任务 | 顺序执行任务 | 稳定负载任务 |
| 内存风险 | 高(可能创建过多线程) | 中(队列可能堆积) | 中(队列可能堆积) |
四、使用注意事项
1. 关闭线程池
java
// 使用完毕后必须关闭,否则程序不会退出
threadPool.shutdown(); // 优雅关闭
// 或
threadPool.shutdownNow(); // 立即关闭
2. 选择合适的线程池
-
任务数量大且执行时间短 →
newCachedThreadPool -
需要保证任务顺序执行 →
newSingleThreadExecutor -
任务执行时间长且数量稳定 →
newFixedThreadPool
3. 线程命名规范
java
// 可以为线程设置有意义的名称,便于调试
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("my-pool-%d").build();
ExecutorService pool = Executors.newFixedThreadPool(5, namedThreadFactory);
五、总结
Executors框架提供的三种线程池各有特点:
-
newCachedThreadPool:弹性伸缩,适合突发性高、短期任务
-
newSingleThreadExecutor:顺序执行,适合需要保证执行顺序的场景
-
newFixedThreadPool:稳定可靠,适合负载均衡的场景
掌握这三种线程池的使用场景和特点,能够帮助我们在实际开发中选择最合适的并发处理方案,提升系统性能和稳定性。