线程与线程池
约 906 字大约 3 分钟
布欧-Lewyon
2026-05-15
线程是操作系统调度的最小单位,Java 中的 Thread 对象对应一个操作系统线程。直接创建大量线程开销很大,因此工程中几乎全部通过线程池(ExecutorService)管理线程。
创建线程的两种方式
继承 Thread
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
}
new MyThread().start();实现 Runnable
Runnable task = () -> System.out.println("Runnable 线程: " + Thread.currentThread().getName());
new Thread(task).start();优先使用 Runnable(或 Callable),因为 Java 是单继承,继承 Thread 会丧失继承其他类的灵活性。
线程状态(生命周期)
NEW ──start()──> RUNNABLE ──> TERMINATED
│
┌────┴────┐
▼ ▼
BLOCKED WAITING/TIMED_WAITING- NEW:
new Thread()创建后,未调用start()。 - RUNNABLE:可运行(可能正在执行或等待 CPU 调度)。
- BLOCKED:等待锁(进入
synchronized代码块但锁被占用)。 - WAITING:
Object.wait()/Thread.join()等待被唤醒。 - TIMED_WAITING:带超时的等待(
Thread.sleep(1000)、wait(1000)等)。 - TERMINATED:
run()执行完毕或抛异常终止。
Thread t = new Thread(() -> {
try { Thread.sleep(500); } catch (InterruptedException e) { }
});
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE(或 TIMED_WAITING)
t.join();
System.out.println(t.getState()); // TERMINATED线程池:ExecutorService
直接 new Thread() 的缺点:
- 创建/销毁线程开销大。
- 线程数量无上限,可能耗尽系统资源。
- 难以管理线程的生命周期。
线程池复用线程,控制并发数。使用 Executors 工厂方法或直接 new ThreadPoolExecutor:
常见线程池
// 固定大小线程池
ExecutorService fixed = Executors.newFixedThreadPool(4);
// 缓存线程池(自动增减线程)
ExecutorService cached = Executors.newCachedThreadPool();
// 单线程池(保证任务串行执行)
ExecutorService single = Executors.newSingleThreadExecutor();
// 定时任务
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.scheduleAtFixedRate(task, 0, 10, TimeUnit.SECONDS);提交任务
// 无返回值
executor.execute(() -> System.out.println("run"));
// 有返回值
Future<Integer> future = executor.submit(() -> {
Thread.sleep(1000);
return 42;
});
Integer result = future.get(); // 阻塞直到拿到结果
Integer result = future.get(5, TimeUnit.SECONDS); // 超时机制关闭线程池
executor.shutdown(); // 不再接受新任务,等待已提交任务完成
executor.awaitTermination(10, TimeUnit.SECONDS); // 等待所有任务结束
if (!executor.isTerminated()) {
executor.shutdownNow(); // 强制停止(中断正在执行的任务)
}始终在应用关闭时合理关闭线程池,否则 JVM 不会退出。
线程池核心参数
直接 new ThreadPoolExecutor 精确控制行为:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize:核心线程数(即使空闲也保留)
5, // maximumPoolSize:最大线程数
60L, // keepAliveTime:超过核心数后的空闲线程存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10), // 工作队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);任务处理流程
提交任务 → 核心线程未满 → 创建核心线程执行
→ 核心线程已满 → 放入工作队列
→ 队列已满 → 创建临时线程(不超过最大线程数)
→ 已达最大线程数 → 执行拒绝策略拒绝策略
| 策略 | 行为 |
|---|---|
AbortPolicy(默认) | 抛 RejectedExecutionException |
CallerRunsPolicy | 由提交任务的线程自己执行 |
DiscardPolicy | 静默丢弃 |
DiscardOldestPolicy | 丢弃队列中最旧的任务然后重试 |
小结
- 实现线程用
Runnable(无返回值)或Callable(有返回值),优先于继承Thread。 - 线程状态:NEW → RUNNABLE → BLOCKED/WAITING → TERMINATED。
- 永远使用线程池(
ExecutorService)管理线程,避免直接new Thread()。 - 线程池参数:corePoolSize、maximumPoolSize、workQueue、keepAliveTime、拒绝策略。
- 易错:
Executors.newCachedThreadPool()的最大线程数是Integer.MAX_VALUE,高并发下可能创建海量线程导致 OOM;生产中建议直接new ThreadPoolExecutor设置合理上限。 - 思考任务:创建一个固定大小 3 的线程池,提交 10 个打印任务,观察哪些线程被复用,最后正确关闭线程池。
上一节:多线程基础
下一节:同步与可见性
