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

Java 多執行緒同步訪問共享可變數據:從 synchronized 到 volatile

     編輯:Java 程式設計入門

問題背景

多執行緒環境下,對共享可變數據的非同步訪問會導致嚴重後果——即使該變數是原子讀寫的(如 boolean、int)。核心問題在於可見性:一個執行緒寫入的值,其他執行緒可能看不到最新值。

反面教材:輪詢失效案例

public class StopThread {
    private static boolean stopRequested; // 非 volatile,非同步
    
    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(() -> {
            int i = 0;
            while (!stopRequested) { // 執行緒可能永遠讀不到 true
                i++;
                System.out.println(i);
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true; // 主執行緒修改
    }
}

結果:背景執行緒無限循環,看不到 stopRequested 的變化。原因:JIT 編譯器可能將讀取優化為寄存器快取,或 CPU 快取未同步至主記憶體。

方案一:synchronized 同步方法

public class StopThread {
    private static boolean stopRequested;
    
    private static synchronized void requestStop() {
        stopRequested = true;
    }
    
    private static synchronized boolean stopRequested() {
        return stopRequested;
    }
    
    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(() -> {
            int i = 0;
            while (!stopRequested()) { // 同步讀取
                i++;
                System.out.println(i);
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        requestStop(); // 同步寫入
    }
}

關鍵點:讀寫雙方都必須同步。僅同步寫入或僅同步讀取均無法保證可見性。synchronized 建立 happens-before 關係,確保記憶體可見性與原子性。

方案二:volatile 關鍵字(輕量級方案)

public class StopThread {
    private static volatile boolean stopRequested; // volatile 保證可見性
    
    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(() -> {
            int i = 0;
            while (!stopRequested) { // 直接讀取,無需同步方法
                i++;
                System.out.println(i);
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true; // 直接寫入
    }
}

深度對比:synchronized vs volatile

特性synchronizedvolatile
可見性✓✓
原子性✓ (複合操作)✗ (單次讀寫)
阻塞可能阻塞不阻塞
適用場景臨界區、複合操作單一變數狀態標記
效能較重極輕量

核心原理:Java 記憶體模型 (JMM)

  • volatile:禁止指令重排序,強制從主記憶體讀寫,建立 StoreStore、LoadLoad 等內存屏障
  • synchronized:監視器鎖,進入/退出同步塊分別具有 acquire/release 語義,保證同步塊內操作對其他執行緒可見

最佳實踐建議

  1. 狀態標記(如停止旗標、初始化完成旗標)→ volatile
  2. 計數器、複雜狀態更新 → synchronized 或 AtomicInteger 等原子類
  3. 複合檢查-執行(如 if (!flag) flag = true) → 必須用 synchronized 或 CAS
  4. 避免 Thread.stop(),採用標記位 + interrupt() 協作式停止

延伸:Thread.stop() 為何不安全?

強制釋放所有鎖,可能留下不一致狀態的物件被其他執行緒訪問,導致數據損壞。Thread.interrupt() 僅設定中斷標記,由執行緒自行處理,安全得多。

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