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

Java 多線程淺析:從理論基礎到同步機制與死鎖防範

     編輯:Java 程式設計入門

一、Java 多線程理論基礎

Java 多線程允許單一程式同時執行多個子任務。線程(輕量級進程)是進程內獨立執行單元,共享進程記憶體空間,帶來更好的互動效能與即時控制能力。雖然強大靈活,但正確使用極具挑戰,即使資深開發者也易誤入陷阱。

辦公室比喻理解多線程

  • CPU = 辦公室職員
  • 單線程 = 職員專心單一任務無干擾
  • 多線程 = 職員同時處理多任務,任務卡住(等資料)則切換其他任務
  • CPU 時間片輪轉極快,體感上所有任務並行推進

三大核心要素

  1. CPU:執行計算
  2. 程式碼:線程行為邏輯
  3. 資料:程式碼操作對象

多線程中,多線程可共享同一程式碼操作不同資料(如會計處理多部門帳目),或各自擁有獨立程式碼與資料。

二、建立線程的兩種方式

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


public class SimpleRunnable implements Runnable {
    private final String message;
    
    public SimpleRunnable(String message) {
        this.message = message;
    }
    
    @Override
    public void run() {
        for (;;) {
            System.out.println(message);
        }
    }
    
    public static void main(String[] args) {
        SimpleRunnable r1 = new SimpleRunnable("Hello");
        Thread t1 = new Thread(r1);
        t1.start();
    }
}

線程啟動從 run() 開始,資料封裝在 Runnable 實例成員中。

方式二:繼承 Thread 類別

覆寫 run(),但佔用唯一繼承名額,靈活度較低。

三、線程四大狀態與管理

狀態說明轉換方法
New (新建)建立未啟動start() → Runnable
Runnable (可執行)等待 CPU 時間片sleep()/wait()/suspend() → Waiting
Waiting (等待/阻塞)等待條件滿足notify()/resume()/時間到 → Runnable
Dead (終止)run() 結束或 stop()不可逆

程式僅當所有線程達到 Dead 狀態時才退出。

四、線程調度與優先級

  • 優先級 1-10,預設 5 (NORM_PRIORITY)
  • Java 採用搶佔式調度:最高優先級線程搶佔 CPU
  • setPriority()/getPriority() 控制
  • 同優先級順序受作業系統時間片影響

線程組管理大量線程

  • ThreadGroup 樹狀結構管理線程集合
  • 支援批量操作:設定優先級、interrupt()、列舉成員
  • 安全隔離:不同群組套用不同安全策略

五、同步機制:保護共享資源

互斥鎖核心

同一時間僅一線程可讀寫共享資料。Java 提供 synchronized 關鍵字:

  • 同步方法:鎖住 this(實例)或 Class 物件(靜態)
  • 同步區塊:synchronized(obj) { ... } 指定鎖物件,粒度更細

讀寫鎖模式(信號標誌)

資料庫場景:讀多寫少。允許並發讀,寫入獨佔。


class Semaphore {
    private static final int EMPTY = 0, READING = 1, WRITING = 2;
    private int state = EMPTY;
    private int readCount = 0;

    public synchronized void readLock() throws InterruptedException {
        while (state == WRITING) wait();
        state = READING;
        readCount++;
    }

    public synchronized void writeLock() throws InterruptedException {
        while (state != EMPTY) wait();
        state = WRITING;
    }

    public synchronized void readUnlock() {
        if (--readCount == 0) {
            state = EMPTY;
            notify();
        }
    }

    public synchronized void writeUnlock() {
        state = EMPTY;
        notify();
    }
}

六、死鎖分析與防範

死鎖形成條件(四必要條件同時滿足)

  1. 互斥:資源獨佔
  2. 佔有並等待:持有資源同時請求新資源
  3. 不可搶佔:資源只能主動釋放
  4. 循環等待:線程環狀等待對方持有的資源

經典死鎖場景


// 線程 1                    // 線程 2
synchronized(lockA) {        synchronized(lockB) {
    synchronized(lockB) {        synchronized(lockA) {
        // 互相等待                // 互相等待
    }                          }
}

防範策略

  1. 固定鎖獲取順序:所有線程按相同順序獲取鎖(如按 hashCode、名稱排序)
  2. 定時嘗試獲取:tryLock(timeout) 避免無限等待
  3. 避免嵌套鎖:重構減少多鎖需求
  4. 使用高層並發工具:ReentrantLock、Semaphore、CountDownLatch 替代低層 synchronized

七、Java 多線程優缺點總結

優點缺點
更好的互動與即時效能跨平台行為差異(依賴 OS 調度)
資源共享簡化線程間通訊同步複雜度高,易死鎖、活鎖、飢餓
利於建模並發業務領域除錯困難,非確定性重現難
多核 CPU 並行加速計算密集任務線程切換、同步開銷降低效能

結語

Java 多線程功能完備但正確使用門檻高。核心在於:共享資料最小化、鎖粒度最小化、鎖順序固定化、優先使用高層並發工具。理解底層原理(Monitor、等待集、狀態轉換)是寫出健壯並發代碼的前提。期待 Java 未來版本持續改進並發模型(如虛擬線程 Project Loom),降低開發者認知負擔。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved