線程是程序執行的最小單元,也是 CPU 調度的基本單位。進程則是操作系統進行資源分配和調度的最小單位。一個進程可以包含多個線程,這些線程共享進程的記憶體空間和資源(如檔案句柄、網路連接等),但各自擁有獨立的程序計數器、棧和局部變量。這種共享機制讓線程間通訊更高效,但也帶來了線程安全問題。
Java 多線程實現的是並發模型(單核環境下)。設計多線程的核心目的是提高資源利用率和響應速度。例如音樂播放器邊播放邊下載,伺服器同時處理多個客戶端請求。
public class MyThread extends Thread {
@Override
public void run() {
for (int x = 0; x < 200; x++) {
System.out.println(x);
}
}
}
// 啟動
public class Demo {
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
}
}
關鍵點:必須調用 start() 而非 run()。start() 會請求 JVM 開啟新線程,並自動調用 run();直接調用 run() 只是普通方法調用,不會開啟新線程。
public class Task implements Runnable {
@Override
public void run() {
// 任務邏輯
}
}
// 啟動
Thread t = new Thread(new Task());
t.start();
優勢:避免單繼承限制,任務與線程控制解耦,方便線程池複用。Thread 本身也實現了 Runnable。
| 方法 | 說明 |
|---|---|
sleep(long ms) | 讓當前線程休眠指定毫秒,釋放 CPU,不釋放鎖。需捕獲 InterruptedException。 |
join() | 等待該線程終止。常用於主線程等待子線程完成。 |
yield() | 禮讓 CPU,當前線程從「運行」轉為「就緒」,讓同優先級線程有機會執行,不保證一定切換。 |
interrupt() | 設置中斷標誌。若線程處於阻塞狀態(sleep/wait/join),會拋出 InterruptedException 並清除標誌;若正在運行,僅設置標誌,需自行檢查 isInterrupted() 退出。 |
優雅停止線程範例:使用 volatile boolean 標誌位。
public class ControlledThread extends Thread {
private volatile boolean running = true;
public void shutdown() { running = false; }
@Override public void run() {
while (running) { /* 執行任務 */ }
}
}
wait()/notify() 屬於鎖(Object)方法,配合 synchronized 使用,詳見下文。
多線程、共享資源、非原子操作(如 D-- 包含讀取、修改、寫回三步)。經典售票例子會出現重複票、負號票。
synchronized(lockObj) { ... },鎖對象可任意,但多線程必須共用同一把鎖。this。Class 對象。// 正確:共享同一把鎖
private final Object lock = new Object();
public void run() {
synchronized (lock) {
if (D > 0) { System.out.println(Thread.currentThread().getName() + "---" + (D--)); }
}
}
兩個或多個線程互相持有對方需要的鎖,導致永久等待。避免策略:固定加鎖順序、使用 tryLock 超時機制、減少鎖嵌套。
共享緩衝區(如 ArrayList)作為鎖。生產者滿則 wait(),消費者空則 wait(),操作完成後 notifyAll() 喚醒等待線程。
// 生產者
synchronized (buffer) {
while (buffer.size() >= MAX) buffer.wait();
buffer.add(item);
buffer.notifyAll();
}
// 消費者
synchronized (buffer) {
while (buffer.isEmpty()) buffer.wait();
Item item = buffer.remove(0);
buffer.notifyAll();
}
頻繁創建/銷毀線程開銷大。線程池預先創建固定數量線程,任務提交到隊列,線程反覆取任務執行,實現「空間換時間」。
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() { public void run() { /* 任務 */ } });
pool.shutdown(); // 關閉線程池
生產環境建議使用 ThreadPoolExecutor 自定義核心參數(核心數、最大數、隊列類型、拒絕策略),避免 Executors 默認配置導致 OOM。