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

Java 多線程經典案例:售票系統的線程安全與同步機制實戰

     編輯:Java 綜合教學

Java 多線程經典案例:售票系統的線程安全與同步機制實戰

「多窗口售票」是理解 Java 多線程併發、線程安全及同步機制的絕佳入門案例。本文從需求分析到代碼實現,層層遞進講解 synchronized 同步代碼塊如何解決數據競爭,並探討死鎖預防原則。

一、需求與初步實現(不安全版)

模擬 4 個窗口同時售賣 100 張票,每張票賣出後總數遞減。初版代碼直接在 run() 循環中操作共享變量 nums:

class Ticket implements Runnable {
    private int nums = 100;
    @Override
    public void run() {
        while (true) {
            if (nums > 0) {
                try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName() + "===" + (nums--));
            } else { break; }
        }
    }
}

問題:nums-- 非原子操作(讀取-修改-寫入),多線程並發下會出現賣出重複票號、負數票號等數據錯誤。

二、引入 synchronized 同步代碼塊(安全版)

class Ticket implements Runnable {
    private int nums = 100;
    @Override
    public void run() {
        while (true) {
            synchronized (this) { // 鎖住當前 Ticket 實例
                if (nums > 0) {
                    try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); }
                    System.out.println(Thread.currentThread().getName() + "===" + (nums--));
                } else { break; }
            }
        }
    }
}

關鍵點:

  • 鎖對象必須是多線程共享的同一實例(此處 this 指向唯一的 Ticket 實例)。
  • 同步塊包裹「檢查條件 + 睡眠 + 輸出 + 遞減」完整邏輯,保證原子性。
  • sleep() 不釋放鎖,避免在睡眠期間其他線程搶佔修改。

三、運行效果驗證

售票運行結果截圖

控制台輸出顯示:4 個線程交替輸出,票號從 100 遞減至 1,無重複、無遺漏、無負數,線程安全達標。

四、進階避坑指南:死鎖預防

代碼註釋提到「避免死鎖問題,同步中嵌套同步,而鎖不同」。核心原則:

  1. 固定加鎖順序:所有線程按相同順序獲取多把鎖。
  2. 縮小同步範圍:只同步必要代碼,減少持鎖時間。
  3. 避免在同步塊中調用外部方法:防止不可控的嵌套鎖。
  4. 使用 ReentrantLock 可中斷鎖/超時機制 作為進階替代。

五、總結

售票案例生動展示了「共享可變狀態 + 非原子操作 = 線程不安全」。synchronized 通過互斥鎖將臨界區串行化,是解決此類問題的基石。實戰中,務必確認鎖對象共享性、同步粒度適中、加鎖順序一致,構建健壯的併發程序。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved