首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Java 多執行緒經典實戰:火車站三窗口同步售票(Runnable + synchronized)

     編輯:關於C語言

問題描述

  • 共享 100 張車票
  • 3 個售票窗口(執行緒)同時賣票
  • 確保:不重賣、不超賣、線程安全

核心設計

  1. 實作 Runnable 介面,共享同一實例 → 共享 tickets 欄位
  2. 使用 synchronized 同步關鍵區段(檢查+賣票+遞減)
  3. 鎖物件:獨立 String 實例(避免鎖 this 受外部干擾)

完整代碼

class TicketSeller implements Runnable {
    private int tickets = 100;
    private final Object lock = new Object();  // 專用鎖物件
    
    @Override
    public void run() {
        while (true) {
            synchronized (lock) {  // 同步塊:原子化「檢查-賣票」
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() 
                        + " 賣出第 " + tickets + " 張票");
                    tickets--;
                } else {
                    break;  // 賣完退出迴圈
                }
            }
            // 模擬賣票耗時,釋放鎖讓其他執行緒進入
            try { Thread.sleep(10); } catch (InterruptedException ignored) {}
        }
    }
}

public class TrainStation {
    public static void main(String[] args) {
        TicketSeller seller = new TicketSeller();  // 單一共享實例
        
        new Thread(seller, "窗口A").start();
        new Thread(seller, "窗口B").start();
        new Thread(seller, "窗口C").start();
    }
}

關鍵點解析

技術點說明
Runnable vs Thread 繼承實作 Runnable 解耦任務與執行緒,支援共享實例、多重實現
共享實例new Thread(seller) 三次傳入同一 seller → 共享 tickets
synchronized(lock)鎖住 lock 物件,確保同一時間只有一執行緒進入關鍵區
檢查與遞減原子化if (tickets > 0) tickets-- 必須在同一同步塊內,否則競態條件
專用鎖物件new Object() 或 new String(),避免外部代碼鎖 this 造成死鎖/阻塞
Thread.sleep模擬真實耗時,增加執行緒切換機會,驗證同步正確性

常見錯誤與修正

// ❌ 錯誤 1:同步塊只包含賣票,不包含檢查
synchronized(lock) { tickets--; }  // 檢查在外面 → 可能賣出負數票

// ❌ 錯誤 2:使用 this 作為鎖,但類可能被外部擴展
synchronized(this) { ... }

// ❌ 錯誤 3:鎖字串常量池物件
synchronized("LOCK") { ... }  // 字串常量全域共享,極易死鎖

// ✅ 正確:專用 final Object lock = new Object();

進階:Java 5+ Lock 介面替代

import java.util.concurrent.locks.*;

class TicketSellerLock implements Runnable {
    private int tickets = 100;
    private final Lock lock = new ReentrantLock();
    
    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() 
                        + " 賣出第 " + tickets + " 張票");
                    tickets--;
                } else break;
            } finally {
                lock.unlock();  // 保證釋放
            }
            // sleep...
        }
    }
}

輸出範例(交錯執行)

窗口A 賣出第 100 張票
窗口B 賣出第 99 張票
窗口C 賣出第 98 張票
窗口A 賣出第 97 張票
...
窗口B 賣出第 1 張票

知識延伸

  • volatile 僅保證可見性,不保證原子性(tickets-- 非原子)
  • AtomicInteger 可無鎖實現:tickets.getAndDecrement()
  • 生產環境:資料庫樂觀鎖/悲觀鎖、分散式鎖
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved