从 Thread 到 Executor 框架
并发编程是 Java 的灵魂之一。从最初的 Thread 类到现代的线程池与异步框架,Java 提供了强大的并发工具链。
本文将系统讲解 Java 多线程的实现方式、线程池架构、核心参数、常见问题与最佳实践。
一、为什么需要并发
在单线程环境下,程序只能顺序执行,CPU 空闲时间(例如等待 IO)无法被充分利用。
并发允许程序同时处理多个任务,提升资源利用率与响应速度。
典型场景:
- 网络请求与数据库查询并行
- 批量计算或大数据处理
- 异步日志、监控上报
- 服务端高并发请求
二、实现多线程的四种方式
| 实现方式 | 关键类/接口 | 特点 |
|---|---|---|
| 1️⃣ 继承 Thread 类 | extends Thread | 直接创建线程,简单但不灵活 |
| 2️⃣ 实现 Runnable 接口 | implements Runnable | 无返回值,推荐使用 |
| 3️⃣ 实现 Callable 接口 | implements Callable<V> | 有返回值,可抛异常 |
| 4️⃣ 使用线程池 Executor 框架 | ExecutorService, Executors, ThreadPoolExecutor | 线程复用、性能优良、可控制性强 |
示例:四种创建方式对比
// 1. 继承 Thread
class MyThread extends Thread {public void run() {System.out.println("Thread 运行中:" + Thread.currentThread().getName());}
}// 2. 实现 Runnable
class MyRunnable implements Runnable {public void run() {System.out.println("Runnable 运行中:" + Thread.currentThread().getName());}
}// 3. 实现 Callable
class MyCallable implements Callable<String> {public String call() {return "Callable 结果:" + Thread.currentThread().getName();}
}// 4. 使用线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new MyRunnable());
Future<String> future = pool.submit(new MyCallable());
System.out.println(future.get());
三、深入理解 Executor 框架
Java 5 引入了 Executor 框架,是多线程的高级抽象层。
Executor → ExecutorService → ThreadPoolExecutor
核心接口结构图:
- **Executor:**定义了任务提交接口。
- **ExecutorService:**扩展生命周期与任务控制(submit、shutdown)。
- **ThreadPoolExecutor:**核心实现类,可自定义线程池行为。
四、线程池的七大参数
public ThreadPoolExecutor(int corePoolSize, // 核心线程数int maximumPoolSize, // 最大线程数long keepAliveTime, // 非核心线程空闲存活时间TimeUnit unit, // 时间单位BlockingQueue<Runnable> workQueue, // 任务队列ThreadFactory threadFactory, // 线程创建工厂RejectedExecutionHandler handler // 拒绝策略
)
| 参数 | 作用 | 说明 |
|---|---|---|
| corePoolSize | 核心线程数 | 线程池始终保持的线程数量 |
| maximumPoolSize | 最大线程数 | 队列满后允许的最大线程数 |
| keepAliveTime | 存活时间 | 非核心线程的空闲销毁时间 |
| unit | 时间单位 | 秒、毫秒等 |
| workQueue | 任务队列 | 缓冲等待执行的任务 |
| threadFactory | 线程工厂 | 自定义线程命名、优先级 |
| handler | 拒绝策略 | 当任务过多时的处理方式(4 种) |
常见拒绝策略:
AbortPolicy:抛出异常(默认)CallerRunsPolicy:由调用线程执行DiscardPolicy:丢弃任务DiscardOldestPolicy:丢弃队首任务
五、任务队列对比
| 队列类型 | 类名 | 特点 |
|---|---|---|
| 有界队列 | ArrayBlockingQueue | 控制最大任务数,防止 OOM |
| 无界队列 | LinkedBlockingQueue | 默认无界,可能堆积过多任务 |
| 优先级队列 | PriorityBlockingQueue | 按优先级执行 |
| 同步队列 | SynchronousQueue | 不缓存任务,直接交付线程 |
六、线程池的执行流程图
七、常见线程池类型
Executors.newFixedThreadPool(n); // 固定大小
Executors.newCachedThreadPool(); // 自动扩容
Executors.newSingleThreadExecutor(); // 单线程顺序执行
Executors.newScheduledThreadPool(n); // 定时任务线程池
⚠️ 建议:生产环境不要直接使用 Executors 创建,容易 OOM。 推荐使用 ThreadPoolExecutor
明确参数,或封装线程池工厂。
八、最佳实践与优化建议
- ✅ 明确任务类型(IO 密集 / CPU 密集)调整线程数
- ✅ 合理设置队列长度与拒绝策略
- ✅ 使用自定义 ThreadFactory 统一命名线程
- ✅ 避免提交长时间阻塞任务
- ✅ 定期监控线程池状态:getPoolSize(), getActiveCount()
九、总结
| 内容 | 重点 |
|---|---|
| 多线程实现方式 | Thread / Runnable / Callable / Executor |
| 核心类 | ThreadPoolExecutor |
| 参数配置 | 7 个核心参数 |
| 常见问题 | 任务堆积、拒绝策略、线程泄漏 |
| 优化方向 | 自定义配置 + 监控线程状态 |
