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

Java 高併發核心:多線程基礎深度解析

     編輯:Java 相關知識

前言

在 Java 高併發編程的領域中,多線程是核心基石。本文基於「煉數成金」課程體系,系統梳理多線程的基本概念、核心操作、線程狀態流轉、同步機制等關鍵知識點,旨在為開發者構建清晰、實用的多線程知識體系。

一、什麼是線程?

線程是進程內部的執行單元。一個進程可以包含多個線程,它們共享進程的內存空間與資源,但擁有獨立的程序計數器、棧和局部變量。使用線程的根本原因在於:進程切換屬於重量級操作,資源消耗極大,併發能力受限;而線程作為更細粒度的調度單元,輕量級且高效,因此成為併發設計的主流選擇。

在 Java 中,線程模型映射到操作系統原生線程(內核級線程),JVM 負責將 Java 線程一一對應到 OS 線程,實現真正的並行執行。

二、線程的基本操作與生命週期

2.1 線程狀態圖解

Java 線程擁有六種核心狀態,狀態流轉如下:

  • NEW:創建線程對象(new Thread()),尚未啟動。
  • RUNNABLE:調用 start() 後進入就緒狀態,等待 CPU 時間片;包含「運行中」與「就緒」兩種子狀態。
  • BLOCKED:線程請求鎖失敗,進入阻塞隊列等待。
  • WAITING:調用 wait()、join() 無參版本,無限期等待其他線程通知。
  • TIMED_WAITING:調用帶超時參數的 sleep()、wait(timeout)、join(timeout),超時自動轉回 RUNNABLE。
  • TERMINATED:run() 方法執行完畢或拋出未捕獲異常,線程結束。

2.2 創建與啟動線程

// 正確方式:啟動新線程
Thread t1 = new Thread("t1") {
    @Override public void run() {
        System.out.println(Thread.currentThread().getName()); // 輸出 t1
    }
};
t1.start();

// 錯誤方式:直接調用 run(),實為普通方法調用,不會開啟新線程
Thread t2 = new Thread("t2") {
    @Override public void run() {
        System.out.println(Thread.currentThread().getName()); // 輸出 main
    }
};
t2.run();

核心區別:start() 會請求 OS 創建新線程並調用 run();直接調用 run() 仍在當前線程執行。

實現 run() 兩種模式:

  1. 繼承 Thread 覆寫 run():簡單直觀,適合簡單邏輯。
  2. 實現 Runnable 接口:推薦做法,符合面向對象設計,邏輯與線程控制解耦,便於線程池複用。

2.3 線程終止:拒絕暴力 stop()

Thread.stop() 已被棄用,因其會強制釋放所有鎖,導致數據不一致且不拋出異常,極難排查。正確做法是採用協作式中斷機制。

2.4 線程中斷:協作式機制

Java 提供三個核心方法:

  • interrupt():設置中斷標誌位為 true。
  • isInterrupted():檢查標誌位,不清除。
  • Thread.interrupted():檢查並清除當前線程標誌位。

關鍵點:中斷只是「請求」,線程需主動檢查標誌位並決定如何響應。

// 非阻塞場景:輪詢標誌位
public void run() {
    while (!Thread.currentThread().isInterrupted()) {
        // 業務邏輯
        Thread.yield();
    }
}

// 阻塞場景:捕獲 InterruptedException 並重新設置標誌位
public void run() {
    while (true) {
        if (Thread.currentThread().isInterrupted()) break;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 重新設置標誌位
            break;
        }
    }
}

2.5 線程掛起與恢復:棄用 suspend()/resume()

這對方法不釋放鎖,極易導致死鎖。例如線程 A 持有鎖並 suspend(),線程 B 嘗試獲取同一鎖會永久阻塞;若 resume() 先於 suspend() 執行,線程將永久掛起。替代方案:使用 wait()/notify() 或 LockSupport.park()/unpark()。

2.6 join() 與 yield()

  • yield():釋放 CPU 時間片,重新參與調度,可能立即再次獲取 CPU。僅用於調試/測試。
  • join():當前線程等待目標線程終止。本質是 while(isAlive()) wait(0);線程結束時 JVM 自動調用 notifyAll() 喚醒等待者。注意:避免在 Thread 實例上直接使用 wait/notify,防止干擾 JVM 內部機制。

三、守護線程

守護線程(Daemon Thread)在後台默默服務,如 GC、JIT 編譯線程。當 JVM 中僅剩守護線程時,JVM 自動退出。

Thread dt = new Thread(() -> {
    while (true) System.out.println("daemon running");
});
dt.setDaemon(true); // 必須在 start() 前設置
dt.start();
// 主線程結束,守護線程隨之終止,無輸出

四、線程優先級

Java 定義三級優先級:MIN_PRIORITY=1、NORM_PRIORITY=5、MAX_PRIORITY=10。優先級高的線程獲得 CPU 時間片機率較大,但不保證必然先執行完,受 OS 調度策略影響。

五、基礎線程同步操作

5.1 synchronized 三種加鎖方式

方式鎖對象適用場景
同步代碼塊 synchronized(obj)指定對象精細控制鎖粒度
同步實例方法當前實例 this實例級別同步
同步靜態方法當前類 Class 對象類級別同步,跨實例生效

5.2 wait()/notify() 核心規則

  • 必須在同步塊/方法中調用,否則拋出 IllegalMonitorStateException。
  • 用什麼鎖,就用什麼對象調用 wait/notify,確保鎖一致性。
  • wait() 釋放鎖並進入等待集;notify() 隨機喚醒一個,notifyAll() 喚醒所有。
  • 被喚醒線程需重新競爭鎖,獲得鎖後從 wait() 返回繼續執行。

總結

多線程是 Java 高併發的基石。掌握線程生命週期、正確的啟動/終止/中斷姿勢、守護線程特性、優先級影響以及同步核心機制,是編寫健壯併發程式的前提。實戰中應優先使用 Runnable + 線程池、協作式中斷、java.util.concurrent 高級組件,避免直接操作底層線程 API。

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