线程池类型
Executors 工厂类
Java 通过 Executors 工厂类提供了几种常用的线程池实现:
1 2 3 4 5 6 7 8 9
| ┌─────────────────────────────────────────────────────────────────┐ │ Executors │ ├─────────────────────────────────────────────────────────────────┤ │ newFixedThreadPool() → 固定大小线程池 │ │ newCachedThreadPool() → 缓存线程池 │ │ newSingleThreadExecutor() → 单线程池 │ │ newScheduledThreadPool() → 定时任务线程池 │ │ newWorkStealingPool() → 工作窃取线程池 (Java 8+) │ └─────────────────────────────────────────────────────────────────┘
|
1. FixedThreadPool(固定大小线程池)
特点
- 核心线程数 = 最大线程数 = 固定值
- 使用无界队列
LinkedBlockingQueue
- 线程不会被回收
源码分析
1 2 3 4 5 6 7 8
| public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor( nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>() ); }
|
使用场景
| 场景 |
说明 |
| CPU 密集型任务 |
线程数 = CPU 核心数 + 1 |
| 负载稳定的服务 |
已知并发量,固定线程数 |
示例
1 2 3 4 5 6 7 8 9
| ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) { executor.submit(() -> { System.out.println(Thread.currentThread().getName() + " 执行任务"); }); } executor.shutdown();
|
风险提示
OOM 风险:使用无界队列,任务堆积可能导致内存溢出
2. CachedThreadPool(缓存线程池)
特点
- 核心线程数 = 0
- 最大线程数 =
Integer.MAX_VALUE(几乎无限)
- 使用
SynchronousQueue(不存储任务,直接传递)
- 空闲线程 60 秒后回收
源码分析
1 2 3 4 5 6 7 8
| public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor( 0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>() ); }
|
使用场景
| 场景 |
说明 |
| 短时异步任务 |
执行时间短,任务量波动大 |
| IO 密集型任务 |
大量等待,需要更多线程 |
示例
1 2 3 4 5 6 7 8 9 10
| ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) { executor.submit(() -> { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(100); } catch (InterruptedException e) {} }); } executor.shutdown();
|
风险提示
线程爆炸:任务提交速度 > 处理速度时,会创建大量线程,可能导致系统崩溃
3. SingleThreadExecutor(单线程池)
特点
- 只有 1 个线程
- 使用无界队列
- 保证任务顺序执行
- 线程异常终止后会创建新线程
源码分析
1 2 3 4 5 6 7 8 9
| public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService( new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>() ) ); }
|
使用场景
| 场景 |
说明 |
| 顺序执行任务 |
日志写入、消息处理 |
| 单线程安全 |
避免并发问题 |
示例
1 2 3 4 5 6 7 8 9
| ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("任务1")); executor.submit(() -> System.out.println("任务2")); executor.submit(() -> System.out.println("任务3"));
executor.shutdown();
|
风险提示
OOM 风险:同 FixedThreadPool,无界队列可能导致内存溢出
4. ScheduledThreadPool(定时任务线程池)
特点
- 支持定时执行和周期执行
- 使用
DelayedWorkQueue(延迟队列)
源码分析
1 2 3 4 5 6 7 8 9 10
| public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
|
核心方法
| 方法 |
说明 |
schedule() |
延迟执行一次 |
scheduleAtFixedRate() |
固定频率执行(不考虑任务耗时) |
scheduleWithFixedDelay() |
固定延迟执行(任务结束后延迟) |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.schedule(() -> { System.out.println("延迟3秒执行"); }, 3, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(() -> { System.out.println("固定频率执行: " + System.currentTimeMillis()); }, 0, 2, TimeUnit.SECONDS);
scheduler.scheduleWithFixedDelay(() -> { System.out.println("固定延迟执行"); try { Thread.sleep(1000); } catch (InterruptedException e) {} }, 0, 2, TimeUnit.SECONDS);
|
scheduleAtFixedRate vs scheduleWithFixedDelay
1 2 3 4 5 6 7 8
| , ------------
, ------------
|
5. WorkStealingPool(工作窃取线程池)
特点(Java 8+)
- 基于 ForkJoinPool 实现 ^forkjoinpool
- 每个线程有自己的双端队列
- 空闲线程会窃取其他线程队列的任务
- 默认线程数 = CPU 核心数
源码分析
1 2 3 4 5 6 7
| public static ExecutorService newWorkStealingPool() { return new ForkJoinPool( Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true ); }
|
工作窃取原理
1 2 3 4 5 6 7
| 线程1的队列: ↑ 从头部取任务执行 线程2的队列: ↑ 自己队列空了,从线程1队列尾部窃取 结果: 线程2窃取任务D执行
|
使用场景
| 场景 |
说明 |
| 递归任务 |
分治算法、树遍历 |
| 并行计算 |
大数据处理、并行流 |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13
| ExecutorService executor = Executors.newWorkStealingPool();
List<Callable<Integer>> tasks = new ArrayList<>(); for (int i = 0; i < 100; i++) { final int num = i; tasks.add(() -> { Thread.sleep(100); return num * num; }); }
List<Future<Integer>> results = executor.invokeAll(tasks);
|
线程池对比总结
| 类型 |
核心线程 |
最大线程 |
队列 |
适用场景 |
| Fixed |
n |
n |
无界 LinkedBlockingQueue |
CPU密集、负载稳定 |
| Cached |
0 |
MAX |
SynchronousQueue |
短任务、IO密集 |
| Single |
1 |
1 |
无界 LinkedBlockingQueue |
顺序执行 |
| Scheduled |
n |
MAX |
DelayedWorkQueue |
定时/周期任务 |
| WorkStealing |
CPU核心数 |
- |
双端队列 |
递归/并行计算 |
最佳实践:自定义线程池
阿里巴巴 Java 开发手册:禁止使用 Executors 创建线程池,应通过 ThreadPoolExecutor 手动创建
原因
| 工厂方法 |
风险 |
| FixedThreadPool |
无界队列可能 OOM |
| SingleThreadExecutor |
无界队列可能 OOM |
| CachedThreadPool |
最大线程数无限,可能创建过多线程 |
| ScheduledThreadPool |
最大线程数无限 |
推荐写法
1 2 3 4 5 6 7 8 9 10 11
| ThreadPoolExecutor executor = new ThreadPoolExecutor( 10, 20, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadFactoryBuilder() .setNameFormat("业务线程-%d") .build(), new ThreadPoolExecutor.CallerRunsPolicy() );
|
线程数设置建议
| 任务类型 |
线程数公式 |
说明 |
| CPU 密集型 |
N + 1 |
N = CPU 核心数 |
| IO 密集型 |
2N 或 N * (1 + W/C) |
W=等待时间,C=计算时间 |
1 2
| int cpuCores = Runtime.getRuntime().availableProcessors();
|
相关链接
- 上一篇:02-线程池原理
- 下一篇:04-ForkJoinPool
- 线程池简介:01-线程池简介