多線程並發訪問共享數據時,若操作非原子性(多條語句組合),可能發生:
典型案例:售票系統 4 個線程賣 100 張票,出現 0、-1、-2 錯票。
synchronized (鎖對象) {
// 操作共享數據的代碼(臨界區)
}
class Ticket implements Runnable {
private static int tickets = 100;
private final Object lock = new Object(); // 共享鎖
public void run() {
while (true) {
synchronized (lock) { // 同步塊包裹判斷+操作
if (tickets > 0) {
try { Thread.sleep(10); } catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName()
+ " sale: " + tickets--);
} else {
break; // 賣完退出
}
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket task = new Ticket(); // 同一任務實例 → 共享 tickets + lock
new Thread(task, "窗口A").start();
new Thread(task, "窗口B").start();
new Thread(task, "窗口C").start();
new Thread(task, "窗口D").start();
}
}
Runnable 實例共享 tickets 與 locksynchronized(lock) 確保「判斷 > 睡眠 > 輸出 > 遞減」原子執行synchronized 加在 run() 方法上 → 效率極低(全程串行)this、Ticket.class,但需確保同一實例/類| 優點 | 缺點 |
|---|---|
| 解決線程安全,數據一致性 | 上下文切換、鎖競爭消耗 CPU |
| 實現簡單,JVM 原生支援 | 易導致死鎖(多鎖嵌套) |
ReentrantLock 支援可中斷、超時、公平鎖、條件變量AtomicInteger 等 CAS 無鎖操作,適合計數器場景ConcurrentHashMap、CopyOnWriteArrayList同步代碼塊是 Java 解決併發安全的基石,核心在於縮小同步範圍、選擇正確鎖對象、確保共享鎖。實戰中優先考慮無鎖數據結構、不可變對象、線程封閉,必要時再使用同步或 Lock。