多線程是 Java 並發編程的基石。本文系統梳理線程創建與終止、互斥同步、線程通信、ThreadLocal 本地變量四大核心模組,結合源碼分析與實戰案例,構建完整的多線程知識體系。
核心關係:Thread 實現了 Runnable 接口,start() 啟動線程,JVM 調用 run() 方法。
| 方式 | 代碼範例 | 優缺點 |
|---|---|---|
| 繼承 Thread 重寫 run() | |
簡單,但無法繼承其他類 |
| 實現 Runnable 重寫 run() | |
推薦:解耦任務與線程,支持線程池、Lambda 表達式 |
棄用 stop()/suspend():粗暴釋放鎖導致數據不一致、死鎖風險。
標準終止模式:interrupt() 設置中斷標誌 → 線程檢查標誌或阻塞方法拋出 InterruptedException → 捕獲異常退出 run()。
class SafeThread extends Thread {
@Override
public void run() {
try {
while (!isInterrupted()) { // 檢查中斷標誌
Thread.sleep(1000); // 阻塞方法可感知中斷
System.out.println(getName() + " running");
}
} catch (InterruptedException e) {
System.out.println(getName() + " 被中斷,安全退出");
}
}
}
public class InterruptDemo {
public static void main(String[] args) throws Exception {
SafeThread t = new SafeThread();
t.start();
Thread.sleep(5000);
t.interrupt(); // 發送中斷信號
}
}
interrupt():設置中斷標誌為 trueisInterrupted():不清除標誌,返回當前狀態interrupted():靜態方法,檢查並清除當前線程標誌NEW → RUNNABLE → RUNNING → TERMINATED
運行態可轉:BLOCKED(鎖等待)、WAITING(無限等待)、TIMED_WAITING(計時等待)
synchronized 修飾實例方法/代碼塊,鎖住 thissynchronized 修飾靜態方法/代碼塊,鎖住 Class 對象// 同步方法(隱式鎖 this)
public synchronized void output(String name) { ... }
// 同步代碼塊(顯式鎖)
public void output(String name) {
synchronized (this) { ... }
}
| 用法 | 場景 | 效果 |
|---|---|---|
| 1 | 線程A訪問obj同步方法 | 線程B訪問obj同步方法/塊 → 阻塞 |
| 2 | 線程A訪問obj同步方法1 | 線程B訪問obj同步方法2 → 阻塞(同一把鎖) |
| 3 | 線程A訪問obj同步方法 | 線程B訪問obj非同步方法 → 不阻塞 |
| 4 | 線程A訪問靜態同步方法 | 線程B訪問同類靜態同步方法/塊 → 阻塞(類鎖) |
wait():釋放鎖,進入等待集合(WAITING)notify():喚醒單個等待線程notifyAll():喚醒所有等待線程必須在synchronized方法/塊中調用,否則拋IllegalMonitorStateException。
| 方法 | 狀態轉換 | 鎖行為 | 用途 |
|---|---|---|---|
| wait() | RUNNING → WAITING | 釋放鎖 | 線程協作 |
| yield() | RUNNING → RUNNABLE | 保持鎖 | 測試讓權(不推薦生產) |
class AlternatePrinter {
private volatile boolean childTurn = false; // volatile 保證可見性
public synchronized void childPrint() throws InterruptedException {
while (!childTurn) {
System.out.println(Thread.currentThread().getName() + " 等待中...");
wait(); // 釋放鎖等待
}
for (int i = 1; i <= 3; i++) {
System.out.println("子線程打印: " + i);
}
childTurn = false;
notify(); // 喚醒主線程
}
public synchronized void mainPrint() throws InterruptedException {
while (childTurn) {
System.out.println(Thread.currentThread().getName() + " 等待中...");
wait();
}
for (int i = 1; i <= 3; i++) {
System.out.println("主線程打印: " + i);
}
childTurn = true;
notify(); // 喚醒子線程
}
}
為每個線程提供獨立的變量副本,線程間互不干擾,避免同步開銷。適用於:數據庫連接、Session 管理、事務上下文傳遞。
Thread 類持有 ThreadLocalMap threadLocalsThreadLocalMap:定製化 HashMap,Key 為 ThreadLocal 對象(弱引用),Value 為線程變量值// set 流程
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) map.set(this, value);
else createMap(t, value);
}
// get 流程
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
Entry e = map.getEntry(this);
if (e != null) return (T)e.value;
}
return setInitialValue(); // 惰性初始化
}
class Passenger {
private String name; private int age;
// ThreadLocal 存儲線程獨有實例
private static ThreadLocal tl = new ThreadLocal<>();
public static Passenger getInstance() {
Passenger p = tl.get();
if (p == null) { p = new Passenger(); tl.set(p); }
return p;
}
// getter/setter...
}
// 業務模塊無需傳參,直接獲取當前線程數據
class ModuleA { void getData() { Passenger.getInstance().getName(); } }
class ModuleB { void getData() { Passenger.getInstance().getAge(); } }
// 測試:3個線程各自寫入、讀取互不干擾
for (int i = 0; i < 3; i++) {
new Thread(() -> {
Passenger.getInstance().setName(randomName());
Passenger.getInstance().setAge(randomAge());
new ModuleA().getData();
new ModuleB().getData();
}).start();
}
volatile + synchronized 保證原子性、可見性remove())多線程編程的本質是狀態管理與協作協議。深入理解 JVM 內存模型、鎖優化、線程池原理,才能駕馭高併發系統設計。