上一篇:Atomic 与 CAS · 下一篇:并发工具类
为什么用线程池
裸 new Thread().start() 每次创建/销毁线程开销大,线程过多耗尽 CPU/内存并加剧 GC。线程池 复用 工作线程,控制并发度。
ThreadPoolExecutor 七大参数
ThreadPoolExecutor pool = new ThreadPoolExecutor( 2, // corePoolSize 核心线程数 4, // maximumPoolSize 最大线程数 60L, TimeUnit.SECONDS, // keepAliveTime 非核心空闲存活时间 new ArrayBlockingQueue<>(100), // workQueue 任务队列 Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() // handler 拒绝策略);执行流程
- 线程数 < core → 新建核心线程执行
- 否则任务入队
- 队列满且线程数 < max → 新建非核心线程
- 队列满且线程数 = max → 拒绝策略
队列已满时
- 无界队列(如
LinkedBlockingQueue默认 Integer.MAX_VALUE):任务排队,一般不新建超过 core 的线程(Fixed 例外看参数) - 有界队列(如
ArrayBlockingQueue):满则扩线程到 max,再满则拒绝
四种 Executors(了解即可,生产推荐手动 ThreadPoolExecutor)
| 方法 | 特点 |
|---|---|
newFixedThreadPool | 固定线程数,无界 LinkedBlockingQueue |
newCachedThreadPool | 可扩缩,SynchronousQueue,线程多时可风险 |
newSingleThreadExecutor | 单线程顺序执行 |
newScheduledThreadPool | 定时/周期任务 |
阿里规范建议:不用 Executors 创建,明确队列边界与拒绝策略。
线程池状态
RUNNING → SHUTDOWN(不接新任务,处理队列)→ STOP → TIDYING → TERMINATED
shutdown() 优雅关闭;shutdownNow() 尝试中断并返回未执行任务。
submit vs execute
| execute | submit | |
|---|---|---|
| 返回值 | void | Future |
| 异常 | 线程 uncaught | 可通过 Future.get() 获取 |
拒绝策略
AbortPolicy(抛异常)、CallerRunsPolicy(调用者线程跑)、DiscardPolicy、DiscardOldestPolicy。
合理线程数
- CPU 密集:约
CPU 核数 + 1 - IO 密集:线程常阻塞,可
2 * CPU 核数或按等待比例估算
公式直觉:等待时间占比越高,线程数可越多。
自定义线程池示例
ExecutorService es = new ThreadPoolExecutor( 4, 8, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), r -> { Thread t = new Thread(r); t.setName("biz-pool-" + t.getId()); return t; }, new ThreadPoolExecutor.CallerRunsPolicy());Fork/Join(预览)
大任务拆小任务并行,再合并结果;注意拆分过深导致线程堆积。详见 并发工具类。
一句话总结
生产用 ThreadPoolExecutor 显式配置 core/max/有界队列/拒绝策略;IO 与 CPU 场景线程数估算不同。