主题
01 - Java 线程基础
Java 并发全景图
┌──────────────────────────────────────────────────────────────────┐
│ Java 并发体系演进 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ JDK 1.0 Thread + synchronized + wait/notify │
│ │ │
│ ▼ │
│ JDK 1.5 java.util.concurrent (JUC) 包 │
│ │ Lock, Atomic, Executor, ConcurrentHashMap │
│ ▼ │
│ JDK 1.8 CompletableFuture (异步编程) │
│ │ Stream 并行流 │
│ ▼ │
│ JDK 19+ Virtual Threads (虚拟线程,类似 Go 的 goroutine) │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 本模块重点:JDK 1.5~1.8 的核心并发 API │ │
│ │ 这些是面试和实际开发中最常用的 │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘1. 创建线程的三种方式
java
// 方式一:继承 Thread 类(不推荐,Java 单继承)
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": 运行中");
}
}
// 方式二:实现 Runnable 接口(推荐)
class MyTask implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": 运行中");
}
}
// 方式三:实现 Callable 接口(可以返回结果 + 抛异常)
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "任务完成";
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
// 方式一
new MyThread().start();
// 方式二
new Thread(new MyTask()).start();
// 方式二(Lambda 简写,最常用)
new Thread(() -> {
System.out.println("Lambda 线程: " + Thread.currentThread().getName());
}).start();
// 方式三
FutureTask<String> future = new FutureTask<>(new MyCallable());
new Thread(future).start();
String result = future.get(); // 阻塞等待结果
System.out.println("Callable 结果: " + result);
}
}三种方式对比:
┌───────────────┬──────────────┬──────────────┬──────────────┐
│ │ Thread │ Runnable │ Callable │
├───────────────┼──────────────┼──────────────┼──────────────┤
│ 返回值 │ 无 │ 无 │ 有 │
│ 异常 │ 不能抛 │ 不能抛 │ 可以抛 │
│ 继承限制 │ 占用继承位 │ 不占 │ 不占 │
│ 推荐程度 │ ✗ │ ✓ │ ✓✓ │
└───────────────┴──────────────┴──────────────┴──────────────┘
实际开发中几乎不直接 new Thread()
→ 用线程池 (ExecutorService) 提交 Runnable/Callable2. 线程生命周期
┌─────────────────────────────────────────────────────────────────┐
│ Java 线程生命周期 │
│ │
│ ┌──────────┐ │
│ │ NEW │ ← new Thread() │
│ └────┬─────┘ │
│ │ .start() │
│ ▼ │
│ ┌──────────────────┐ │
│ ┌───────────▶│ RUNNABLE │◀───────────┐ │
│ │ │ (就绪 + 运行中) │ │ │
│ │ └───┬──────────┬───┘ │ │
│ │ │ │ │ │
│ │ ┌────────────┘ └──────────┐ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ ┌─────┴────────┐ ┌─────────┴──────────┐ │
│ │ BLOCKED │ │ WAITING │ │
│ │ │ │ │ │
│ │ 等待获取锁 │ │ wait() │ │
│ │ synchronized │ │ join() │ │
│ │ 进入同步块 │ │ LockSupport.park() │ │
│ └──────────────┘ └────────────────────┘ │
│ │ │ │
│ │ │ │
│ │ ┌─────────────────────┴──────┐ │
│ │ │ TIMED_WAITING │ │
│ │ │ │ │
│ │ │ sleep(ms) │ │
│ │ │ wait(ms) │ │
│ │ │ join(ms) │ │
│ │ └────────────────────────────┘ │
│ │ │ │
│ │ │ 超时 / 被唤醒 │
│ └───────────────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ TERMINATED │ ← run() 执行完毕 │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
状态转换摘要:
NEW ─start()─▶ RUNNABLE ─run()完毕─▶ TERMINATED
│
├─ synchronized 竞争锁 ─▶ BLOCKED
├─ wait()/join() ─▶ WAITING
└─ sleep(ms)/wait(ms) ─▶ TIMED_WAITING3. synchronized 关键字
java
public class SynchronizedDemo {
private int count = 0;
// 方式一:同步方法(锁 = this 对象)
public synchronized void increment() {
count++;
}
// 方式二:同步块(更灵活,可以指定锁对象)
private final Object lock = new Object();
public void incrementWithBlock() {
synchronized (lock) {
count++;
}
}
// 方式三:静态同步方法(锁 = Class 对象)
private static int globalCount = 0;
public static synchronized void staticIncrement() {
globalCount++;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo demo = new SynchronizedDemo();
// 启动 100 个线程同时递增
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
demo.increment();
}
});
threads[i].start();
}
// 等待所有线程完成
for (Thread t : threads) {
t.join();
}
System.out.println("最终计数: " + demo.count); // 100000
}
}synchronized 的锁机制:
实例方法: 静态方法:
synchronized void foo() static synchronized void bar()
┌──────────────┐ ┌───────────────────┐
│ this 对象 │ │ Class 对象 │
│ ┌────────┐ │ │ ┌──────────────┐ │
│ │ 对象头 │ │ │ │ MyClass.class│ │
│ │ Mark │ │ │ │ │ │
│ │ Word │ │ │ └──────────────┘ │
│ └────┬───┘ │ └────────┬──────────┘
│ │ │ │
│ 锁状态标记 │ 锁状态标记
└──────────────┘ ─────────────────────
同步块:
synchronized (lockObj) {
// 临界区代码
}
┌─────────────────────────────────────────────────┐
│ 线程 A lockObj 线程 B │
│ │ │ │ │
│ │──monitorenter──▶│ │ │
│ │ 获取锁 ✓ │ │ │
│ │ │◀──monitorenter──│ │
│ │ │ 获取锁 ✗(阻塞) │ │
│ │ 执行临界区 │ 等待... │ │
│ │──monitorexit──▶│ │ │
│ │ 释放锁 │──────唤醒────────▶│ │
│ │ │ 获取锁 ✓ │ │
└─────────────────────────────────────────────────┘synchronized 锁升级(JDK 6+ 优化)
┌──────────────────────────────────────────────────────────────┐
│ synchronized 锁升级过程 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ 无锁 │──▶│ 偏向锁 │──▶│ 轻量级锁 │──▶│ 重量级锁│ │
│ │ │ │ │ │ │ │ │ │
│ │ 没有竞争 │ │ 只有一个 │ │ 少量竞争 │ │ 激烈竞争│ │
│ │ │ │ 线程访问 │ │ CAS 自旋 │ │ OS 挂起│ │
│ └──────────┘ └──────────┘ └──────────┘ └────────┘ │
│ │
│ 性能: 快 ────────────────────────────────────────▶ 慢 │
│ │
│ 偏向锁: 第一个获取锁的线程直接标记,不用 CAS │
│ 如果一直是同一个线程,几乎零开销 │
│ │
│ 轻量级锁: 其他线程来竞争,CAS 自旋等待 │
│ 自旋一定次数后升级为重量级锁 │
│ │
│ 重量级锁: 竞争激烈,线程挂起等待(OS 级别) │
│ 性能最差,但公平 │
│ │
│ ⚠️ 锁只能升级不能降级 │
└──────────────────────────────────────────────────────────────┘4. wait / notify 机制
java
public class ProducerConsumer {
private final Queue<Integer> queue = new LinkedList<>();
private final int capacity = 5;
// 生产者
public synchronized void produce(int item) throws InterruptedException {
while (queue.size() == capacity) {
wait(); // 队列满了,等待消费者消费
}
queue.add(item);
System.out.println("生产: " + item + " 队列大小: " + queue.size());
notifyAll(); // 通知消费者
}
// 消费者
public synchronized int consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 队列空了,等待生产者生产
}
int item = queue.poll();
System.out.println("消费: " + item + " 队列大小: " + queue.size());
notifyAll(); // 通知生产者
return item;
}
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
// 生产者线程
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try { pc.produce(i); } catch (InterruptedException e) { break; }
}
}).start();
// 消费者线程
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try { pc.consume(); } catch (InterruptedException e) { break; }
}
}).start();
}
}wait/notify 通信流程:
生产者 共享队列 消费者
│ ┌──────────┐ │
│──produce()──▶ │ [1,2,3, │ ◀──consume()──│
│ │ 4,5] │ │
│ │ 满了! │ │
│──wait()──┐ └──────────┘ ┌──consume()
│ (释放锁) │ │ 取出 1
│ 挂起... │ ┌──────────┐ │
│ │ │ [2,3,4,5]│ │
│ │ └──────────┘ │
│ │◀────notifyAll()───────────┘
│ 被唤醒 │ │
│ 重新竞争锁│ │
│──produce() │
│ │
⚠️ 重要规则:
┌────────────────────────────────────────────────────┐
│ 1. wait/notify 必须在 synchronized 块中调用 │
│ 2. 用 while 循环检查条件,不要用 if(虚假唤醒) │
│ 3. 优先用 notifyAll() 而非 notify() │
│ 4. 实际开发中更推荐用 JUC 的工具替代 │
└────────────────────────────────────────────────────┘5. 对比 Go 的并发
┌─────────────────────────────────────────────────────────┐
│ Java 线程 vs Go Goroutine │
├──────────────────────┬──────────────────────────────────┤
│ Java │ Go │
├──────────────────────┼──────────────────────────────────┤
│ new Thread() │ go func() │
│ ~1MB 栈 │ ~2KB 栈 │
│ 1:1 (OS线程) │ M:N (用户态调度) │
│ synchronized │ sync.Mutex │
│ wait/notify │ channel │
│ ExecutorService │ goroutine + channel │
│ CompletableFuture │ goroutine + channel │
│ 显式共享内存 │ "通过通信共享内存" │
├──────────────────────┴──────────────────────────────────┤
│ │
│ Java: 更成熟的工具链,更多的并发数据结构 │
│ Go: 更轻量,语言级支持,写起来更简单 │
│ │
│ 两者都要掌握: │
│ Java 主战场 = 企业级后端、大数据 │
│ Go 主战场 = 基础设施、云原生、高性能网络服务 │
│ │
└─────────────────────────────────────────────────────────┘下一节: 02 - JMM 内存模型 — 理解 volatile、happens-before、指令重排