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

Java 多線程安全入門:識別臨界資源與 synchronized 互斥鎖

     編輯:Java 相關知識

多線程帶來的隱形危機

多線程讓程式能「一心多用」,但也引入了經典的線程安全問題。當多個線程同時讀寫同一份資料(如變數、檔案、資料庫連線)時,若缺乏協調機制,就會產生「髒讀」、「丟失更新」等難以復現的錯誤。這類被多線程共同存取的資源,稱為臨界資源。

直觀演示:兩個線程搶著減同一個數

我們建立一個 Data 類封裝 int n = 60,兩個 DataThread 共享同一個 Data 實例,各自循環 10 次:印出數值 → 遞減 1 → 睡 200ms。

public class Data { public int n = 60; }

public class DataThread extends Thread {
    private Data data; private String name;
    public DataThread(Data data, String name) { this.data=data; this.name=name; start(); }
    public void run() {
        for (int i=0;i<10;i++) {
            System.out.println(name + ":" + data.n);
            data.n--;
            Thread.sleep(200);
        }
    }
}

混亂的輸出結果

線程1:60
線程2:60   ← 重複讀到 60
線程2:58
線程1:58   ← 重複讀到 58
...
線程2:47   ← 甚至出現中間值 47

原因:線程 1 讀取 n=60 尚未印出/遞減,CPU 切換到線程 2,線程 2 也讀到 n=60 並遞減為 59,線程 1 恢復執行遞減為 58……操作被打斷、交錯,導致邏輯錯誤。

現實類比:銀行雙通道取款

帳戶餘額 1000 元,存折與 ATM 卡同時取 800 元。若無同步,兩邊都讀到 1000 → 都判定可取 → 都扣 800 → 最終餘額 -600,銀行虧損。這就是典型的競態條件。

解決方案:synchronized 互斥鎖

Java 提供 synchronized 關鍵字,為方法或程式碼區塊加鎖。規則簡單:**同一個物件實例上,同一時間只允許一個執行緒進入被 synchronized 修飾的區域**。

改良版:將「讀取+遞減」封裝為原子操作

public class Data2 {
    public int n = 60;
    // 整個方法加鎖,確保「讀-改-寫」不可分割
    public synchronized void action(String name) {
        System.out.println(name + ":" + n);
        n--;
    }
}

public class Data2Thread extends Thread {
    private Data2 data; private String name;
    public Data2Thread(Data2 data, String name) { this.data=data; this.name=name; start(); }
    public void run() {
        for (int i=0;i<10;i++) {
            data.action(name); // 呼叫同步方法
            Thread.sleep(200);
        }
    }
}

有序的輸出結果

線程1:60
線程2:59
線程2:58
線程1:57
...  ← 每個數值只被處理一次,嚴格遞減

深入理解:鎖的是誰?

  • synchronized 修飾實例方法 → 鎖是 this(當前物件實例)。
  • synchronized 修飾靜態方法 → 鎖是 ClassName.class(類物件)。
  • synchronized(obj) { ... } 同步區塊 → 鎖是 obj(需為同一實例)。

⚠️ 關鍵:不同實例互不干擾。只有「多線程操作同一個物件實例」時,鎖才生效。

銀行櫃台比喻

櫃員 = 共享物件,客戶 = 線程。
客戶 A 正在辦理業務(持有鎖),客戶 B 只能排隊等待(阻塞)。即使 A 中途接電話(進入阻塞態),B 依然不能插隊——這就是 synchronized 的「獨佔性」。

總結與展望

synchronized 是解決線程安全最基礎、最直接的工具,適用於「單純互斥存取」場景。但面對「條件等待、生產消費協作」等複雜場景時,單純互斥會造成資源浪費(忙等待)。下一篇將介紹 同步機制——透過 wait()/notify() 實現線程間的高效協作。

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