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

Java 多線程程式設計完整指南:從核心原理到實戰模式

     編輯:Java 程式設計入門

一、理解多線程本質

多線程允許程式中並發執行多個指令流,每個指令流稱為一個線程,彼此獨立。線程又稱輕量級進程,擁有獨立執行控制,由作業系統調度。關鍵差異:線程無獨立儲存空間,隸屬進程內所有線程共享同一記憶體空間,使線程間通訊遠較進程簡單。

多線程執行邏輯上「同時」,物理上視 CPU 核心數而定。單核 CPU 透過時間片輪轉模擬並發,切換極快用戶無感知。多線程與單線程最大設計差異在於:各線程控制流獨立導致代碼亂序執行,引發線程調度、同步等核心挑戰。

二、Java 實現多線程的兩大標準路徑

路徑一:繼承 Thread 類別,覆寫 run()

public class MyThread extends Thread {
    private int count = 1;
    private final int number;

    public MyThread(int num) {
        this.number = num;
        System.out.println("建立線程 " + number);
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("線程 " + number + ": 計數 " + count);
            if (++count == 6) return;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new MyThread(i + 1).start();
        }
    }
}

優點:直觀、符合繼承習慣。
缺點:Java 單一繼承限制,若類別已繼承其他父類(如 Applet)則無法使用。

路徑二:實作 Runnable 介面(推薦)

public class MyRunnable implements Runnable {
    private int count = 1;
    private final int number;

    public MyRunnable(int num) {
        this.number = num;
        System.out.println("建立線程 " + number);
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("線程 " + number + ": 計數 " + count);
            if (++count == 6) return;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new MyRunnable(i + 1)).start();
        }
    }
}

優點:避免繼承限制、利於封裝、同一 Runnable 實例可被多線程共享(適合賣票等場景)。
缺點:需額外建立 Thread 實例、若需多組不同邏輯仍需多類別。

核心區別總結

維度extends Threadimplements Runnable
繼承限制佔用唯一繼承名額無繼承限制
邏輯與控制分離混合分離(Runnable 只管邏輯)
共享邏輯實例困難天然支援
取得當前線程thisThread.currentThread()

三、線程四大狀態與生命週期

  1. 新建:new Thread() 已建立,未呼叫 start()
  2. 可執行:start() 後,等待 CPU 時間片,包含 Running 與 Ready
  3. 阻塞:無法獲得 CPU,等待條件(鎖、I/O、sleep、wait)
  4. 終止:run() 正常返回、拋出未捕獲異常、呼叫 stop()(不推薦)

四、線程優先級與調度

  • 範圍:1 (MIN_PRIORITY) ~ 10 (MAX_PRIORITY),預設 5 (NORM_PRIORITY)
  • 高優先級獲得 CPU 機率較大,但非絕對佔有
  • getPriority()/setPriority() 存取
  • Java 採用搶佔式調度:最高優先級線程搶佔 CPU

五、線程同步核心機制

synchronized 關鍵字:兩種用法


// 1. 同步方法:鎖住 this(實例方法)或 Class 物件(靜態方法)
public synchronized void accessVal(int newVal) { ... }

// 2. 同步區塊:指定鎖物件,粒度更細、靈活度更高
synchronized (syncObject) {
    // 受保護代碼
}

鎖機制本質

  • 每個物件擁有一把內建鎖(Monitor)
  • 同步方法/區塊執行前必須獲得鎖,執行完釋放
  • 同一時間每個物件鎖僅能被一線程持有
  • 確保所有訪問共享資料的方法均為 synchronized 即可避免衝突

常見錯誤:將大方法或 run() 設為 synchronized

導致該線程終身持有鎖,其他同步方法永無機會執行。解法:提煉關鍵區塊為獨立同步方法,或使用同步區塊縮小鎖範圍。

六、線程阻塞與協作機制

方法所屬類別釋放鎖典型用途
sleep(ms)Thread否定時輪詢、延遲重試
suspend()/resume()Thread否已棄用,易死鎖
yield()Thread否主動讓出 CPU,保持可執行態
wait()/notify()Object是線程間協作、生產消費者模式

wait/notify 核心規則(必須遵守,否則 IllegalMonitorStateException)

  1. 必須在 synchronized 方法/區塊內呼叫
  2. 呼叫物件的鎖必須為當前線程持有
  3. wait() 釋放鎖並進入等待集;notify() 隨機喚醒一線程,notifyAll() 喚醒全部

生產者-消費者經典範式


class Buffer {
    private final List<Object> queue = new ArrayList<>();
    private final int capacity;

    public synchronized void put(Object item) throws InterruptedException {
        while (queue.size() == capacity) wait();  // 滿等待
        queue.add(item);
        notifyAll();  // 喚醒消費者
    }

    public synchronized Object take() throws InterruptedException {
        while (queue.isEmpty()) wait();  // 空等待
        Object item = queue.remove(0);
        notifyAll();  // 喚醒生產者
        return item;
    }
}

七、守護線程

  • 非核心線程,僅為其他線程提供後台服務(如 GC、JIT 編譯)
  • 所有非守護線程結束 → JVM 退出,無視守護線程是否仍在運行
  • setDaemon(true) 必須在 start() 前呼叫

八、線程組

  • Java 獨有概念,ThreadGroup 物件管理線程集合
  • 樹狀結構,根為 system 群組
  • 支援批量操作:設定優先級、中斷、列舉成員
  • 安全隔離:不同群組可套用不同安全策略

九、總結:何時真正需要多線程?

多線程核心在於「多代碼塊並發執行」且「代碼亂序執行」。判斷標準:

  1. 不需並發 → 不需多線程
  2. 需並發但不需亂序(可序列化執行)→ 單線程循環更簡單高效
  3. 完全符合並發+亂序特徵 → 多線程機制發揮價值

切勿濫用多線程,線程切換開銷、同步複雜度、除錯困難度都是實實在在的成本。

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