每個執行緒擁有兩個計數器 count1、count2,在 run() 中依序遞增並顯示。邏輯上它們應始終相等,但引入 Watcher 監視執行緒後,卻發現兩者頻繁出現不等狀態(標籤顯示 "Unsynched")。
public void run() {
while (true) {
t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));
try { sleep(500); } catch (InterruptedException e) {}
}
}
public void synchTest() {
Sharing1.incrementAccess(); // 訪問計數
if (count1 != count2) l.setText("Unsynched");
}
雖然 count1++ 與 count2++ 相鄰,但 JVM 可能在兩行之間**暫停當前執行緒**,切換至 Watcher 執行緒執行 synchTest()。此時 count1 已更新而 count2 尚未更新,導致檢測到不一致。
這揭示了多執行緒環境下的核心問題:**無法預測執行緒何時運行、何時被掛起**。共享資源在關鍵區段必須受保護,否則會出現競態條件。
public synchronized void run() { ... } // 整個方法同步,粒度較粗
// 或細粒度同步塊
public void run() {
while (true) {
synchronized(this) {
t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));
}
try { sleep(500); } catch (InterruptedException e) {}
}
}
private final AtomicInteger count1 = new AtomicInteger(0);
private final AtomicInteger count2 = new AtomicInteger(0);
public void run() {
while (true) {
int c1 = count1.incrementAndGet();
int c2 = count2.incrementAndGet();
t1.setText(String.valueOf(c1));
t2.setText(String.valueOf(c2));
// AtomicInteger 保證單次操作原子性,但兩個獨立原子操作仍非事務性
// 若需強一致性,仍需額外同步或使用 LongAdder + 單一計數器設計
}
}
Atomic*、ConcurrentHashMap、Lock、Semaphore 等比低層 synchronized 更安全、效能更好。本範例雖簡單,卻道出了多執行緒程式設計的核心挑戰:**共享資源的協同存取**。理解「檢查-執行」非原子性、掌握同步機制與現代併發工具,是寫出正確、高效併發程式的必修課。