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

Java 多線程基礎:物件與變數併發訪問深度解析

     編輯:Java 相關知識

在開發多線程程序時,若每個線程處理的任務互不相關,開發難度相對較低。但在實際場景中,多線程往往需要同時訪問同一個物件或變數,這時若缺乏同步控制,極易導致處理結果與預期不符。本文將深入探討如何實現線程安全的併發訪問,確保多線程環境下數據的一致性與完整性。

一、方法級同步控制

當多個線程同時訪問同一物件的方法時,若無同步機制,執行結果將不可預測。使用 synchronized 關鍵字修飾方法,可確保同一時間僅有一個線程能執行該方法,其他線程需等待鎖釋放後方可進入。

1. 未加同步的線程不安全示例

public class HasSelfPrivateNum {
    private int num = 0;
    
    public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

測試時,線程 A 先進入方法並睡眠 2 秒,期間線程 B 修改了 num 為 200,導致線程 A 輸出時讀取到被覆蓋的值,出現 a num=200 的錯誤結果。

2. 加入 synchronized 後的線程安全版本

synchronized public void addI(String username) {
    // 方法體不變
}

執行結果變為:a set over! a num=100 b set over! b num=200,符合預期。這證明 synchronized 方法實質是物件鎖,鎖住的是當前物件實例。不同物件實例調用同步方法互不干擾。

3. 物件鎖的進一步驗證

同一物件的兩個同步方法,線程分別調用時仍會串行執行,證明鎖針對的是整個物件,而非單個方法。

4. 臟讀問題與解決

若讀取方法未加同步,寫入方法在執行過程中被打斷,讀取線程可能獲取到未寫入完成的中間狀態數據(臟讀)。解決方案是對讀寫方法均加 synchronized。

5. 可重入鎖特性

Java 的內建鎖支援可重入:線程持有鎖時可再次獲取同一鎖,避免死鎖。此特性在繼承關係中同樣生效,子類同步方法可調用父類同步方法。但需注意:同步修飾符不會被繼承,子類覆寫父類同步方法時若未加 synchronized,則失去同步效果。同步方法拋出未捕獲異常時會自動釋放鎖。

二、同步代碼塊

同步方法鎖定整個方法,若方法包含耗時操作(如 I/O、複雜計算),會降低併發性能。同步代碼塊 synchronized(obj) { ... } 允許僅對關鍵代碼加鎖,縮小鎖範圍,提升吞吐量。

鎖對象選擇原則

  • 可使用 this、任意共享物件、類實例
  • 避免使用 String 常量,因字符串常量池機制可能導致非預期的鎖共享

同步代碼塊與同步方法的交互特性

  1. 多線程執行 synchronized(x){} 同步塊時互斥
  2. 其他線程執行 x 物件的 synchronized 方法時同步
  3. 其他線程執行 x 物件的 synchronized(this) 代碼塊時同步

性能優化示例

將耗時的遠程調用移出同步塊,僅在賦值共享變數時加鎖,兩線程併發耗時從 6 秒降至 3 秒。此外,對不同業務邏輯使用不同鎖對象,可實現方法間的異步並行執行。

三、類級鎖同步

類鎖針對類的所有實例生效,實現方式有二:

  • synchronized 修飾靜態方法
  • synchronized(ClassName.class) 同步代碼塊

類鎖與物件鎖互不干擾:靜態同步方法之間串行,但與實例同步方法可並行執行。類鎖同樣具備可重入特性。

注意事項

鎖定的是物件的內存地址,修改鎖物件的屬性值不會改變鎖的身份,同步機制依然有效。

四、volatile 關鍵字:可見性保證

多線程訪問共享變數時,線程會將主內存變數複製到線程工作內存操作,寫回主內存存在延遲,導致可見性問題。volatile 強制線程每次從主內存讀取最新值,寫入後立即刷新主內存。

核心機制

  • read/load:主內存 → 工作內存
  • use/assign:執行代碼修改變數(可多次)
  • store/write:工作內存 → 主內存

關鍵限制:volatile 僅保證可見性,不保證原子性。複合操作(如 count++)在併發下仍會出現數據競爭,需配合 synchronized 或原子類使用。

總結

Java 同步機制核心在於:

  • synchronized:提供互斥訪問與可見性,支援方法鎖、代碼塊鎖、類鎖,具備可重入特性
  • volatile:輕量級可見性保證,適用於單次讀寫操作,不可替代同步鎖

實際開發中應根據業務場景選擇:追求簡單正確用同步方法,高併發性能敏感用同步代碼塊縮小鎖粒度,單純狀態標誌可用 volatile。完整代碼示例請參考 GitHub 倉庫 char02 目錄,多線程通訊內容見 char03。

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