通過經典「多窗口售票」案例,直觀演示線程不安全現象與 synchronized 兩種用法的修復效果。
class TicketSource implements Runnable {
private int ticket = 10;
public void run() {
for (int i=1; i<50; i++) {
if (ticket > 0) {
Thread.sleep(1000); // 放大併發窗口
System.out.println(Thread.currentThread().getName()
+ "號窗口賣出" + this.ticket-- + "號票");
}
}
}
}
// 三線程並發:new Thread(mt,"1").start()...
輸出錯誤:出現重複票號、負數票號、總賣出超過 10 張。

public synchronized void sale() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName()
+ "號窗口賣出" + this.ticket-- + "號票");
}
}
public void run() {
for (int i=1; i<50; i++) {
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
sale(); // 委託同步方法
}
}
public void run() {
for (int i=1; i<50; i++) {
synchronized (this) { // 鎖定當前實例
if (ticket > 0) {
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()
+ "號窗口賣出" + this.ticket-- + "號票");
}
}
}
}
兩種方案均輸出正確:1-10 號票各賣出一次,無重無漏。

TicketSource 實例)synchronized 必須覆蓋「檢查條件 + 修改狀態」完整原子操作,錯位會導致保護失效