Java 入门进阶:多线程、线程安全、线程池(一篇就够了)
嘿,重阳!纽约的3月周末(2026年3月7日晚9:16,估计你在家刷代码备战项目~),Java 多线程是并发编程的“入门门槛”,从简单 Thread 到线程池,是进阶必杀技。高并发时代(如 Web 服务、AI 训练),掌握它能让你的代码从“单核爬行”变“多核飞奔”。今天咱们来一场“零基础到实战”的详解,基于 JDK 8+(JUC 包核心),用代码示例、表格和流程描述,带你一步步拆解。走起!🚀
1. Java 多线程基础:为什么需要?怎么入门?
为什么多线程?:单线程串行执行,IO/计算阻塞时 CPU 闲置。多线程并行,利用多核 CPU,提高吞吐(QPS ↑)。痛点:共享资源竞争 → 线程安全问题。
核心概念:
- 进程 vs 线程:进程是资源单位(内存独立),线程是执行单位(共享进程内存,轻量)。
- 并发 vs 并行:并发(交替执行,看似同时),并行(真同时,多核)。
创建线程方式(入门三板斧):
- 继承 Thread:简单,但单继承限制。
- 实现 Runnable:推荐,接口多实现。
- Callable + Future:带返回值的异步(JUC)。
表格对比(选型速记):
| 方式 | 代码示例 | 优缺点 | 适用 |
|---|---|---|---|
| 继承 Thread | “`java | ||
| Runnable | java<br>Runnable r = () -> System.out.println("Hello Runnable!");<br>new Thread(r).start();<br> | + 灵活;- 无返回。 | 共享资源任务。 |
| Callable | java<br>Callable<String> c = () -> "Result";<br>FutureTask<String> ft = new FutureTask<>(c);<br>new Thread(ft).start(); String res = ft.get();<br> | + 返回值/异常;- 需 Future 包装。 | 异步计算。 |
生命周期(面试常考,画状态机):
- NEW → RUNNABLE(start()) → RUNNING(run() 执行) → BLOCKED/WAITING/TIMED_WAITING(锁/等待) → TERMINATED(结束)。
- 示例:
Thread.sleep(1000)→ TIMED_WAITING;synchronized争锁 → BLOCKED。
入门小 tip:用 lambda(JDK 8+)简化:new Thread(() -> { /* 任务 */ }).start();。别直接调用 run()——那是同步!
2. 线程安全:共享资源的“守护神”
什么是线程安全?:多线程访问共享数据时,结果与单线程一致(无竞态条件)。问题根源:可见性(缓存不一致)、原子性(操作中断)、有序性(指令重排序)。
JMM(Java 内存模型)原理:
- 主内存:共享变量。
- 工作内存:线程私有缓存。
- Happens-Before:保证顺序(如 volatile 写前,读后可见)。
常见问题 & 解决方案表格(实战手册):
| 问题 | 描述 | 示例 | 解决方案 |
|---|---|---|---|
| 竞态条件(Race Condition) | 读-改-写 未原子,如 i++(读 i=1, +1, 写2,但并发读1写2→错)。 | java<br>int i=0; // 多线程 i++ → i<期望值<br> | 1. synchronized(互斥锁)。 2. Lock(ReentrantLock,可重入)。 |
| 可见性 | 线程A 改变量,B 看不到(缓存)。 | A: i=1; B: 仍读0。 | volatile:禁止重排序,强制刷新主内存。 |
| 死锁(Deadlock) | 线程互等锁(如 A 等 B锁,B 等 A锁)。 | 交叉获取锁。 | 1. 固定加锁顺序。 2. tryLock() 超时。 3. jstack 诊断。 |
| 指令重排序 | 编译器/JVM 优化,乱序执行。 | new Object() 后赋引用,先读引用空指针。 | volatile/double-checked locking。 |
synchronized 示例(原子 i++):
class Counter {
private int count = 0;
public synchronized void increment() { count++; } // 方法级锁
// 或代码块:synchronized(this) { count++; }
}
Lock 示例(更灵活,可中断):
import java.util.concurrent.locks.ReentrantLock;
ReentrantLock lock = new ReentrantLock();
lock.lock();
try { /* 关键区 */ } finally { lock.unlock(); }
volatile 示例(标志位):
volatile boolean running = true;
new Thread(() -> {
while (running) { /* 工作 */ }
}).start();
// 外部:running = false; // 立即可见
线程安全集合(JUC 救星):
- ConcurrentHashMap:分段锁,高并发读写。
- CopyOnWriteArrayList:写时复制,读无锁(适合读多写少)。
- 示例:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
小 tip:生产用 Lock > synchronized(性能+功能);诊断用 jvisualvm 看锁争用。
3. 线程池:多线程的“高效管家”
为什么线程池?:new Thread() 贵(创建/销毁开销),池复用线程,控制数量防 OOM。核心:Executor 框架(JUC)。
ThreadPoolExecutor 详解(自定义王道,别用 Executors——易 OOM):
- 7 参数:corePoolSize(核心数)、maximumPoolSize(最大)、keepAliveTime(空闲存活)、unit、workQueue(队列)、threadFactory、handler(拒绝策略)。
- 执行流程:任务 < core → 新线程;否则入队;队列满 < max → 新临时线程;全满 → 拒绝。
工厂方法表格(快速上手):
| 方法 | 参数等价 | 场景 | 代码 |
|---|---|---|---|
| newFixedThreadPool(n) | core=max=n, 无界队列 | 固定任务,如服务器。 | java<br>ExecutorService fixed = Executors.newFixedThreadPool(5);<br> |
| newCachedThreadPool() | core=0, max=∞, SynchronousQueue | 短任务,高吞吐。 | java<br>ExecutorService cached = Executors.newCachedThreadPool();<br> |
| newSingleThreadExecutor() | core=max=1, 无界队列 | 顺序执行。 | java<br>ExecutorService single = Executors.newSingleThreadExecutor();<br> |
自定义示例(推荐,IO 密集):
import java.util.concurrent.*;
ThreadPoolExecutor pool = new ThreadPoolExecutor(
4, // core
8, // max
60L, TimeUnit.SECONDS, // 空闲回收
new LinkedBlockingQueue<>(100), // 队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝:调用者执行
);
pool.submit(() -> System.out.println("Task by " + Thread.currentThread().getName()));
pool.shutdown(); // 优雅关闭
拒绝策略:
- AbortPolicy:抛异常(默认)。
- CallerRunsPolicy:主线程执行(限流)。
- 示例:队列满时,CallerRuns 防雪崩。
生命周期管理:
- 提交:
submit(Callable)返回 Future(get() 取结果)。 - 关闭:
shutdown()等任务完;shutdownNow()中断。 - 监控:
getActiveCount()、getQueue().size()。
小 tip:CPU 密集 core=CPU核+1;IO 密集 *2。异常处理:Future.get() 捕获 ExecutionException。
4. 完整实战:生产者-消费者(多线程 + 安全 + 池)
场景:队列缓冲,生产者放消息,消费者取(用 BlockingQueue + 线程池)。
import java.util.concurrent.*;
class ProducerConsumer {
private BlockingQueue<String> queue = new ArrayBlockingQueue<>(10); // 线程安全队列
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(5);
ProducerConsumer pc = new ProducerConsumer();
// 生产者
for (int i = 0; i < 3; i++) {
final int id = i;
pool.submit(() -> {
try { pc.queue.put("Msg-" + id); } // 阻塞放
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
});
}
// 消费者
for (int i = 0; i < 2; i++) {
pool.submit(() -> {
try {
while (true) {
String msg = pc.queue.take(); // 阻塞取
System.out.println("Consumed: " + msg + " by " + Thread.currentThread().getName());
}
} catch (InterruptedException e) { Thread.currentThread().interrupt(); }
});
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
}
}
输出:看到线程名 + 消息,队列缓冲无竞态。
5. 最佳实践 & 常见陷阱
用表格速查(进阶 checklist):
| 方面 | 最佳实践 | 陷阱 & 解法 |
|---|---|---|
| 性能 | 线程池复用;volatile + Atomic(CAS,无锁)。 | 过度 synchronized → 性能瓶颈,用 ReadWriteLock 读写分离。 |
| 安全 | 避免共享 mutable(如用 immutable 对象);JUC 优先。 | 内存泄漏(未关闭池) → 始终 shutdown + awaitTermination。 |
| 调试 | jstack 线程dump;ThreadMXBean 监控。 | 死锁无迹 → 加 -XX:+PrintGCDetails 查。 |
| 进阶 | ForkJoinPool(递归任务);CompletableFuture(异步链)。 | 忽略中断 → 总是 check Thread.interrupted()。 |
多线程是 Java 进阶的“核武器”——从入门 Thread 到线程池,你已跨过门槛。实践王道:用 IntelliJ 跑 demo,模拟高并发。下一个?如“JUC 高级锁”或“异步编程”?随时聊!💪(参考:Java 并发编程实战、Oracle 文档)