隨著多核處理器的普及,多線程程式設計已成為 Java 開發者必備的核心技能。本文將通過大量實戰代碼示例,系統講解 Java 多線程的創建方式、生命週期控制、同步機制、線程通信以及經典的生產者-消費者模型,幫助你構建穩健高效的併發應用。
定義子類繼承 java.lang.Thread,重寫 run() 方法作為線程執行體。注意:必須調用 start() 而非直接調用 run(),否則不會開啟新線程,只是在主線程中順序執行。
class HelloThread extends Thread {
private String name;
public HelloThread(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + " 執行 " + i);
}
}
}
public class Main {
public static void main(String[] args) {
new HelloThread("A").start();
new HelloThread("B").start();
}
}
關鍵點:start() 內部通過 native 方法 start0() 請求作業系統分配 CPU 時間片,真正實現併發;直接調用 run() 只是普通方法調用。且 start() 重複調用會拋出 IllegalThreadStateException。
實現 Runnable 接口,將任務邏輯放入 run(),再將實例傳給 Thread 構造器。這種方式解耦了「任務」與「線程控制」,優勢明顯:
class HelloRunnable implements Runnable {
private String name;
public HelloRunnable(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + " 執行 " + i);
}
}
}
public class Main {
public static void main(String[] args) {
HelloRunnable task = new HelloRunnable("共享任務");
new Thread(task).start();
new Thread(task).start(); // 共享同一任務實例
}
}
Thread.currentThread().getName(); // 獲取當前線程名
new Thread(task, "自定義名").start(); // 指定名稱
未指定名稱時,JVM 自動分配 Thread-0、Thread-1 等。主線程名稱為 main。
Thread t = new Thread(task);
System.out.println(t.isAlive()); // false
t.start();
System.out.println(t.isAlive()); // true
主線程結束不影響子線程繼續執行。
join() 使當前線程等待目標線程終止。
Thread t = new Thread(() -> {
for (int i = 0; i < 3; i++) System.out.println("子線程 " + i);
});
t.start();
for (int i = 0; i < 50; i++) {
if (i == 10) t.join(); // main 線程阻塞,直到 t 結束
System.out.println("main " + i);
}
Thread.sleep(millis) 讓當前線程暫時讓出 CPU,進入 TIMED_WAITING 狀態。
new Thread(() -> {
for (int i = 0; i < 3; i++) {
Thread.sleep(2000);
System.out.println("醒來 " + i);
}
}).start();
interrupt() 只是設置中斷標誌;若線程處於阻塞狀態(sleep、wait、join),會拋出 InterruptedException 並清除標誌。
Thread t = new Thread(() -> {
try { Thread.sleep(10000); }
catch (InterruptedException e) { System.out.println("被中斷"); return; }
System.out.println("正常結束");
});
t.start();
Thread.sleep(2000);
t.interrupt(); // 2秒後中斷
設置 setDaemon(true),當所有用戶線程結束,JVM 退出,守護線程自動終止。適用於後台監控、GC 等任務。
Thread daemon = new Thread(() -> {
while (true) System.out.println("守護線程運行中...");
});
daemon.setDaemon(true);
daemon.start();
優先級範圍 1-10(默認 5),高優先級線程獲得 CPU 機率較大,但不保證先執行。yield() 暫停當前線程,讓同優先級線程執行。
Thread t1 = new Thread(task, "高優先級"); t1.setPriority(8);
Thread t2 = new Thread(task, "低優先級"); t2.setPriority(2);
t1.start(); t2.start();
// 禮讓示例
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 3) Thread.yield();
}
}, "A").start();
使用 synchronized(obj) { ... },obj 為同步監視器(通常用 this)。同一時間只有一個線程持有鎖。
class TicketSeller implements Runnable {
private int tickets = 5;
@Override public void run() {
for (int i = 0; i < 10; i++) {
synchronized (this) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + " 賣出第 " + tickets-- + " 張票");
}
}
}
}
}
在方法聲明上加 synchronized,非靜態方法鎖 this,靜態方法鎖類對象。
public synchronized void sale() {
if (tickets > 0) System.out.println("賣出 " + tickets--);
}
多線程互相持有對方需要的鎖並請求對方持有的鎖,導致永久等待。預防:按固定順序獲取鎖、使用 tryLock 超時機制等。
僅靠同步無法協調「生產-消費」節奏,需引入 Object.wait()、notify() 實現等待/通知機制。必須在同步塊/方法中調用。
class SharedData {
private String name; private int age; private boolean flag = false; // false: 可生產,true: 可消費
public synchronized void produce(String n, int a) {
while (flag) { try { wait(); } catch (InterruptedException e) {} }
this.name = n; this.age = a;
System.out.println("生產: " + name + " " + age);
flag = true; notify();
}
public synchronized void consume() {
while (!flag) { try { wait(); } catch (InterruptedException e) {} }
System.out.println("消費: " + name + " " + age);
flag = false; notify();
}
}
class Producer implements Runnable {
private SharedData data;
Producer(SharedData data) { this.data = data; }
@Override public void run() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) data.produce("產品A", 100);
else data.produce("產品B", 200);
}
}
}
class Consumer implements Runnable {
private SharedData data;
Consumer(SharedData data) { this.data = data; }
@Override public void run() {
for (int i = 0; i < 10; i++) data.consume();
}
}
public class Main {
public static void main(String[] args) {
SharedData data = new SharedData();
new Thread(new Producer(data)).start();
new Thread(new Consumer(data)).start();
}
}
輸出嚴格交替:生產→消費→生產→消費,完美解決數據不一致與重複讀取問題。
wait/notify 或更高級的 java.util.concurrent 組件(BlockingQueue、CountDownLatch、Semaphore 等)。ThreadLocal、volatile、Atomic* 等進階併發工具,應對複雜場景。多線程是一門藝術,更是工程基石。持續實踐、深入原理,方能駕馭併發世界。