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

多線程共享 Integer 變量陷阱與正確解法

     編輯:Java 綜合教學

問題場景

三線程輪流打印 A、B、C 共 10 次。初始嘗試用共享 Integer gData = 0 配合 synchronized(gData) 協調,結果僅打印 A。

根因分析

Integer 為不可變類,內部 final int value。每次 gData++ 實際是:

gData = Integer.valueOf(gData.intValue() + 1); // 新建 Integer 物件

線程 A 更新後持有新物件引用,線程 B/C 仍持有舊物件(值仍為 0),導致條件 gData % 3 == n 永不滿足。

驗證:在迴圈中打印 gData 可見 B/C 線程讀到的值未變。

解法一:AtomicInteger(推薦)

AtomicInteger gData = new AtomicInteger(0);
// 線程中
synchronized (gData) {
    if (gData.get() % 3 == n) {
        System.out.print(info + " ");
        gData.incrementAndGet(); // 原子更新
        i++;
    }
}

AtomicInteger 內部用 CAS 保證原子性,物件引用不變,所有線程見到同一物件的最新值。

解法二:wait/notify 經典協作模式

Object A = new Object(), B = new Object(), C = new Object();
new MyThread(C, A, "A").start(); // prev=C, curr=A
new MyThread(A, B, "B").start();
new MyThread(B, C, "C").start();

class MyThread extends Thread {
    private final Object prev, curr;
    private final String info;
    public MyThread(Object prev, Object curr, String info) { ... }
    public void run() {
        for (int i = 10; i > 0; i--) {
            synchronized (prev) {
                synchronized (curr) {
                    System.out.print(info + " ");
                    curr.notify(); // 喚醒下一個
                }
                prev.wait(); // 等待上一個喚醒
            }
        }
    }
}

三把鎖環狀傳遞,嚴格保證執行順序。需注意啟動順序與微小 sleep 確保進入 wait 狀態。

核心教訓

  • 不可變物件不可作同步鎖:更新會產生新物件,破壞共享鎖語義
  • 共享可變狀態請用線程安全容器(Atomic*、ConcurrentHashMap)或同步器(CountDownLatch、Semaphore)
  • 經典 wait/notify 雖基礎,但能精準控制線程協作順序
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved