多線程是 Java 語言的核心特性之一,它允許程式在同一時間內並行執行多個任務,極大地提升了應用程式的響應速度與資源利用率。本文將系統性地介紹 Java 多線程的基本概念、線程生命週期、優先級調度、實現方式以及線程同步機制,並輔以代碼範例幫助開發者深入理解。
在探討多線程之前,需釐清三個易混淆的概念:
.class 檔案。以 QQ 客戶端為例:客戶端本身是「程式」;登入 QQ 啟動一個「進程」;與好友聊天發送訊息則對應進程中的一條「線程」。
Java 使用 java.lang.Thread 類及其子類對象來代表線程。一個線程從誕生到消亡,會經歷以下五種狀態:
當使用 new Thread() 或繼承 Thread 類創建實例時,線程進入新建狀態。此時已分配記憶體與資源,但尚未啟動。
調用 start() 方法後,線程進入就緒隊列,等待 CPU 排程。具備執行條件,一旦獲得 CPU 時間片即可進入運行狀態。
線程獲得 CPU 執行權,開始執行 run() 方法中的代碼邏輯。所有線程操作的核心代碼均應編寫在 run() 方法內。
運行中的線程因等待資源(如 I/O、鎖、Sleep)而暫時放棄 CPU,進入阻塞狀態。阻塞原因消除後,線程重新回到就緒隊列等待調度。
run() 方法正常執行完畢,或因異常、stop() 強制終止,線程結束生命週期,不可再重新啟動。
Java 線程採用搶佔式調度,優先級高的線程更容易獲得 CPU。優先級分三級:
MIN_PRIORITY = 1(低)NORM_PRIORITY = 5(普通,主線程預設)MAX_PRIORITY = 10(高)可透過 setPriority(int) 修改,但最終調度仍取決於作業系統。
class MyThread extends Thread {
public void run() {
// 線程執行邏輯
}
}
// 使用
MyThread t = new MyThread();
t.start(); // 啟動線程,進入就緒狀態
class MyRunnable implements Runnable {
public void run() {
// 線程執行邏輯
}
}
// 使用
Thread t = new Thread(new MyRunnable());
t.start();
Runnable 方式解耦了線程任務與線程控制,便於資源共享,且避免單繼承限制。
| 方法 | 說明 |
|---|---|
sleep(long ms) | 讓當前線程休眠指定毫秒,不釋放鎖 |
yield() | 讓出 CPU,重新參與調度 |
join() | 等待該線程終止 |
isAlive() | 判斷線程是否存活 |
多線程同時存取共享資料時會發生線程安全問題。Java 提供 synchronized 關鍵字實現同步:
public synchronized void method() { ... }synchronized(obj) { ... }同步機制確保同一時間只有一個線程能執行被保護的代碼區段,其他線程需等待鎖釋放。
public class BankDemo extends Applet implements Runnable {
int money = 100;
Thread accountant, cashier;
TextArea log1, log2;
public void init() {
accountant = new Thread(this);
cashier = new Thread(this);
log1 = new TextArea(20, 8);
log2 = new TextArea(20, 8);
add(log1); add(log2);
}
public void start() {
accountant.start();
cashier.start();
}
// 同步方法:同一時間只允許一個線程存取
public synchronized void access(int amount) {
if (Thread.currentThread() == accountant) {
for (int i = 0; i < 3; i++) {
money += amount;
try { Thread.sleep(1000); } catch (Exception e) {}
log1.append("\n" + money);
}
} else if (Thread.currentThread() == cashier) {
for (int i = 0; i < 2; i++) {
money -= amount / 2;
try { Thread.sleep(1000); } catch (Exception e) {}
log2.append("\n" + money);
}
}
}
public void run() {
if (Thread.currentThread() == accountant || Thread.currentThread() == cashier) {
for (int i = 0; i < 3; i++) access(30);
}
}
}
當線程需等待某條件滿足時,可在同步區塊中調用 wait() 釋放鎖並進入等待池;條件滿足後,其他線程調用 notify()(喚醒單一線程)或 notifyAll()(喚醒所有等待線程)使其重新競爭鎖。
wait():線程等待,釋放鎖notify():喚醒一個等待線程notifyAll():喚醒所有等待線程這三個方法屬於 Object 類,必須在同步上下文中調用,否則拋出 IllegalMonitorStateException。
Java 多線程機制為並發程式設計提供了強大支撐。掌握線程生命週期、優先級調度、實現方式、同步與通信,是開發高效、穩定並發應用的基石。實務中應根據業務場景合理選擇同步粒度,避免過度同步導致效能下降,並善用 java.util.concurrent 套件中的高階工具(如 Lock、CountDownLatch、ThreadPoolExecutor)簡化開發。