首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多執行緒核心概念:Thread、Runnable、同步化、wait/notify、死結

     編輯:Java 相關知識

前言

多執行緒是 Java 核心特性之一。本文系統整理:執行緒建立兩種方式、生命週期狀態、同步化機制、wait/notify 協作模式、常見問題與最佳實踐。注意:Java 5+ 引入 java.util.concurrent 高階 API,實務開發優先使用 ExecutorService、Concurrent 集合、Lock、CountDownLatch 等,本文基礎 API 僅供理解底層原理。

建立執行緒兩種方式

方式一:繼承 Thread 類別

public class MyThread extends Thread {
    @Override
    public void run() {
        // 執行緒工作內容
        for (int i = 0; i < 5; i++) {
            System.out.println(getName() + ": " + i);
        }
    }
}

// 使用
MyThread t1 = new MyThread();
t1.setName("Thread-A");
t1.start();  // 呼叫 start(),非 run()!

方式二:實作 Runnable 介面(推薦)

public class MyTask implements Runnable {
    @Override
    public void run() {
        // 工作內容
    }
}

// 使用
Thread t2 = new Thread(new MyTask(), "Thread-B");
t2.start();

// Java 8+ Lambda
Thread t3 = new Thread(() -> {
    System.out.println("Lambda 執行緒");
}, "Thread-C");
t3.start();

比較

特性extends Threadimplements Runnable
繼承限制單一繼承,無法繼承其他類別可繼承其他類別,彈性高
職責分離執行緒控制與工作邏輯混在一起工作邏輯獨立,可重複使用
執行緒池相容不可直接提交可提交給 ExecutorService

執行緒生命週期狀態

NEW → RUNNABLE → RUNNING
                ↑    ↓
           BLOCKED/WAITING/TIMED_WAITING
                ↓
           TERMINATED
狀態說明進入原因
NEW建立未啟動new Thread()
RUNNABLE就緒/執行中start()、時間片輪轉、喚醒
BLOCKED阻塞等待監視器鎖進入 synchronized 區塊被佔用
WAITING無限期等待Object.wait()、Thread.join()、LockSupport.park()
TIMED_WAITING限時等待Thread.sleep()、wait(timeout)、join(timeout)
TERMINATED執行結束run() 返回、拋出未捕獲例外

常用控制方法

sleep() - 讓出 CPU、不釋放鎖

try {
    Thread.sleep(1000);           // 毫秒
    Thread.sleep(1000, 500000);   // 毫秒 + 奈秒
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();  // 重設中斷旗標
}

yield() - 讓出 CPU、不保證調度

Thread.yield();  // 暗示調度器:我暫時不搶 CPU,同優先級其他執行緒可執行

join() - 等待其他執行緒結束

Thread t = new Thread(() -> { ... });
t.start();
t.join();           // 無限等待
t.join(5000);       // 最多等 5 秒

interrupt() - 中斷執行緒

// 中斷沉睡/等待中的執行緒
Thread t = new Thread(() -> {
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        System.out.println("被中斷了");
        Thread.currentThread().interrupt();  // 重要:重設旗標
    }
});
t.start();
Thread.sleep(100);
t.interrupt();  // 觸發 InterruptedException

同步化

synchronized 關鍵字

// 1. 同步實例方法(鎖住 this)
public synchronized void deposit(int amount) {
    balance += amount;
}

// 2. 同步靜態方法(鎖住 Class 物件)
public static synchronized void staticMethod() { ... }

// 3. 同步區塊(指定鎖物件,粒度更細)
public void transfer(Account from, Account to, int amount) {
    synchronized (from) {
        synchronized (to) {
            // 轉帳邏輯
        }
    }
}

內建鎖特性

  • 可重入:同一執行緒可重複獲得同一鎖
  • 互斥:同一時間只有一個執行緒持有
  • 釋放:正常返回或拋出例外時自動釋放

wait()/notify()/notifyAll() - 執行緒協作

必須在 synchronized 區塊/方法中呼叫,否則拋 IllegalMonitorStateException。

經典生產者-消費者模式

public class BoundedBuffer {
    private final List buffer = new ArrayList<>();
    private final int capacity;

    public BoundedBuffer(int capacity) { this.capacity = capacity; }

    public synchronized void put(T item) throws InterruptedException {
        while (buffer.size() == capacity) {
            wait();  // 釋放鎖並等待,被 notify 喚醒後重新檢查條件
        }
        buffer.add(item);
        notifyAll();  // 喚醒所有等待執行緒(消費者)
    }

    public synchronized T take() throws InterruptedException {
        while (buffer.isEmpty()) {
            wait();
        }
        T item = buffer.remove(0);
        notifyAll();  // 喚醒生產者
        return item;
    }
}

關鍵模式

  1. while 迴圈檢查條件:防止虛假喚醒
  2. notifyAll 優於 notify:避免喚醒錯誤類型執行緒導致卡死
  3. 狀態改變後通知:修改共享狀態 → notifyAll

死結與預防

死結條件(四必要條件同時滿足)

  1. 互斥:資源獨佔
  2. 持有並等待:持有資源同時請求新資源
  3. 不可搶佔:資源只能自願釋放
  4. 循環等待:T1 等 T2 持有的資源,T2 等 T1 持有的資源

死結範例

Object lockA = new Object();
Object lockB = new Object();

// Thread 1
synchronized (lockA) {
    Thread.sleep(100);
    synchronized (lockB) { ... }  // 死結!
}

// Thread 2
synchronized (lockB) {
    Thread.sleep(100);
    synchronized (lockA) { ... }  // 死結!
}

預防策略

  • 固定鎖順序:所有執行緒按相同順序獲取鎖(如 hashCode 排序)
  • 使用 tryLock(timeout):ReentrantLock 支援限時獲取,失敗可釋放已持有鎖重試
  • 縮小同步範圍:只在必要程式碼加鎖
  • 避免巢狀鎖:重構設計減少多重鎖需求

daemon 執行緒(守護執行緒)

Thread t = new Thread(() -> { ... });
t.setDaemon(true);  // 必須在 start() 前呼叫
t.start();
  • JVM 結束條件:所有非 daemon 執行緒結束
  • daemon 執行緒適合:背景清理、監控、定時任務
  • 注意:daemon 執行緒在 JVM 關閉時會被強制終止,不執行 finally 區塊

Java 5+ 高階 API 快速導覽(實務首選)

總結

  • 優先用 Runnable + ExecutorService,少用繼承 Thread
  • 理解六大狀態與轉換,善用 join/interrupt 控制流程
  • synchronized 是基礎,掌握 wait/notifyAll 協作模式(while 迴圈 + notifyAll)
  • 死結預防:固定鎖順序、tryLock、縮小同步範圍
  • 實務專案直接使用 java.util.concurrent,別重造輪子
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
類別/介面用途
ExecutorService執行緒池管理,替代手動 new Thread()
Future / CompletableFuture非同步運算結果
CountDownLatch等待多個任務完成
CyclicBarrier多執行緒同步屏障
Semaphore限制同時存取數量
ConcurrentHashMap執行緒安全 Map
CopyOnWriteArrayList讀多寫少安全 List
BlockingQueue生產者-消費者佇列
ReentrantLock / ReadWriteLock進階鎖功能
ThreadLocal執行緒區域變數