public class TestMain {
int i = 0; // 共享計數器
public static void main(String[] args) {
TestMain c = new TestMain();
Runnable worker = new Worker(c);
for (int k = 0; k < 200; k++) new Thread(worker).start();
while (true) {
System.out.println(c.i); // 觀察值
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
}
}
}
class Worker implements Runnable {
TestMain c;
Worker(TestMain c) { this.c = c; }
public void run() {
try { Thread.sleep((int)(Math.random() * 25)); } catch (InterruptedException ignored) {}
c.i++; // 關鍵:非原子操作!
}
}
預期 200,實際僅約 50% 機率達標,其餘為 199、198... 並發寫入導致更新丟失。
i++ 三步曲i 到執行緒工作記憶體。多執行緒交錯執行時,Read-Write 間隙導致彼此覆蓋對方結果。Java 不保證 ++ 原子性 (除非 volatile 配合 CAS 迴圈,但語法不支援)。
| 方案 | 代碼示例 | 優點 | 缺點 |
|---|---|---|---|
| synchronized | synchronized(c) { c.i++; } | 語法簡單、隱式鎖、JVM 優化成熟 (偏向鎖、輕量級鎖) | 爭用激烈時效能下降、死鎖風險 |
| ReentrantLock | lock.lock(); try { c.i++; } finally { lock.unlock(); } | 可中斷、公平鎖、條件變數、超時嘗試 | 需手動 unlock、語法較繁瑣 |
| AtomicInteger (JDK 5+) | AtomicInteger i = new AtomicInteger(0); i.incrementAndGet(); | 無鎖 CAS、高併發效能優、API 豐富 (addAndGet, compareAndSet) | 僅支援單一變數、ABA 問題極少數場景需注意 |
| LongAdder (JDK 8+) | LongAdder sum = new LongAdder(); sum.increment(); | 高併發下優於 AtomicInteger (分段計數、最後聚合) | 記憶體佔用略高、無 getAndSet 等精確語義 |
AtomicInteger/LongAdder:簡潔、高效、語義明確,專為計數場景設計。synchronized:多變數邏輯、非計數型業務原子操作。volatile int i; i++;:volatile 僅保證可見性,不保證複合操作原子性。import java.util.concurrent.atomic.AtomicInteger;
public class SafeCounter {
static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
try { Thread.sleep((int)(Math.random()*25)); } catch (InterruptedException ignored) {}
count.incrementAndGet();
};
Thread[] threads = new Thread[200];
for (int k=0; k<200; k++) { threads[k] = new Thread(task); threads[k].start(); }
for (Thread t : threads) t.join();
System.out.println("Final: " + count.get()); // 穩定輸出 200
}
}