多線程允許程式中並發執行多個指令流,每個指令流稱為一個線程,彼此獨立。線程又稱輕量級進程,擁有獨立執行控制,由作業系統調度。關鍵差異:線程無獨立儲存空間,隸屬進程內所有線程共享同一記憶體空間,使線程間通訊遠較進程簡單。
多線程執行邏輯上「同時」,物理上視 CPU 核心數而定。單核 CPU 透過時間片輪轉模擬並發,切換極快用戶無感知。多線程與單線程最大設計差異在於:各線程控制流獨立導致代碼亂序執行,引發線程調度、同步等核心挑戰。
public class MyThread extends Thread {
private int count = 1;
private final int number;
public MyThread(int num) {
this.number = num;
System.out.println("建立線程 " + number);
}
@Override
public void run() {
while (true) {
System.out.println("線程 " + number + ": 計數 " + count);
if (++count == 6) return;
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new MyThread(i + 1).start();
}
}
}
優點:直觀、符合繼承習慣。
缺點:Java 單一繼承限制,若類別已繼承其他父類(如 Applet)則無法使用。
public class MyRunnable implements Runnable {
private int count = 1;
private final int number;
public MyRunnable(int num) {
this.number = num;
System.out.println("建立線程 " + number);
}
@Override
public void run() {
while (true) {
System.out.println("線程 " + number + ": 計數 " + count);
if (++count == 6) return;
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new MyRunnable(i + 1)).start();
}
}
}
優點:避免繼承限制、利於封裝、同一 Runnable 實例可被多線程共享(適合賣票等場景)。
缺點:需額外建立 Thread 實例、若需多組不同邏輯仍需多類別。
| 維度 | extends Thread | implements Runnable |
|---|---|---|
| 繼承限制 | 佔用唯一繼承名額 | 無繼承限制 |
| 邏輯與控制分離 | 混合 | 分離(Runnable 只管邏輯) |
| 共享邏輯實例 | 困難 | 天然支援 |
| 取得當前線程 | this | Thread.currentThread() |
new Thread() 已建立,未呼叫 start()start() 後,等待 CPU 時間片,包含 Running 與 Readyrun() 正常返回、拋出未捕獲異常、呼叫 stop()(不推薦)MIN_PRIORITY) ~ 10 (MAX_PRIORITY),預設 5 (NORM_PRIORITY)getPriority()/setPriority() 存取
// 1. 同步方法:鎖住 this(實例方法)或 Class 物件(靜態方法)
public synchronized void accessVal(int newVal) { ... }
// 2. 同步區塊:指定鎖物件,粒度更細、靈活度更高
synchronized (syncObject) {
// 受保護代碼
}
導致該線程終身持有鎖,其他同步方法永無機會執行。解法:提煉關鍵區塊為獨立同步方法,或使用同步區塊縮小鎖範圍。
| 方法 | 所屬類別 | 釋放鎖 | 典型用途 |
|---|---|---|---|
| sleep(ms) | Thread | 否 | 定時輪詢、延遲重試 |
| suspend()/resume() | Thread | 否 | 已棄用,易死鎖 |
| yield() | Thread | 否 | 主動讓出 CPU,保持可執行態 |
| wait()/notify() | Object | 是 | 線程間協作、生產消費者模式 |
IllegalMonitorStateException)synchronized 方法/區塊內呼叫wait() 釋放鎖並進入等待集;notify() 隨機喚醒一線程,notifyAll() 喚醒全部
class Buffer {
private final List<Object> queue = new ArrayList<>();
private final int capacity;
public synchronized void put(Object item) throws InterruptedException {
while (queue.size() == capacity) wait(); // 滿等待
queue.add(item);
notifyAll(); // 喚醒消費者
}
public synchronized Object take() throws InterruptedException {
while (queue.isEmpty()) wait(); // 空等待
Object item = queue.remove(0);
notifyAll(); // 喚醒生產者
return item;
}
}
setDaemon(true) 必須在 start() 前呼叫ThreadGroup 物件管理線程集合system 群組多線程核心在於「多代碼塊並發執行」且「代碼亂序執行」。判斷標準:
切勿濫用多線程,線程切換開銷、同步複雜度、除錯困難度都是實實在在的成本。