Java 多執行緒售票示例

hapday發表於2024-11-03

1、售票任務類:

package com.joyupx.cms.example.globalLock5;

import cn.hutool.core.date.DateTime;
import lombok.extern.slf4j.Slf4j;

import static java.lang.Thread.sleep;

/**
 * 售票任務
 * @author hapday
 * @since 0.0.1
 */
@Slf4j
public class SellTicketTask6 implements Runnable {

    // 餘票數
    private int ticketsLeft = 50;
    // 票號,用來表示已賣了多少張票。
    private int ticketNumber = 1;

    /**
     * 建立【鎖標識】物件,用在全域性鎖中,是多個執行緒爭搶的物件。
     * 誰搶到了它就意味著誰擁有的主動權,具體到這裡就是本輪執行任務的權利,繼續處在【執行中(running)】狀態。
     * 沒有搶到【鎖標識】的執行緒被迫進入到【阻塞中(blocking)】狀態,等待持有鎖的執行緒釋放了鎖後繼續搶【鎖標識】。
     */
    private LockFlag lockFlag = new LockFlag();

    /**
     * 自定義類實現【可執行介面(Runnable)】介面後必須實現其【執行(run)】方法
     * 並且【執行(run)】方法是自動執行的,具體來說是由虛擬機器來主動呼叫的。
     */
    @Override
    public void run() {
        /**
         * 當還有餘票時
         */
        while (0 < ticketsLeft) {
            try {
                /**
                 * 用以模擬每售出一張票後的客戶切換。
                 */
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            /**
             * 全域性鎖
             * 以自定義物件做為鎖,多個執行緒同時搶它。
             */
            synchronized (lockFlag) {
                /**
                 * 防止一票多賣、防止多出票、防止漏票。
                 */
                if (0 < ticketsLeft) {
                    log.info("{} 正在出售第 {} 張車票。", Thread.currentThread().getName(), ticketNumber);
                    // 每賣出一張票,餘票數減一,直至餘票數為零時為止。
                    ticketsLeft--;
                    // 每賣出一張票,票號加一。
                    ticketNumber++;

                    /**
                     * 每賣 5 張票做一個分割。
                     */
                    if (ticketNumber % 5 == 1) {
                        System.out.println("\t" + DateTime.now());
                    }
                }
            }
        }
    }

}

/**
 * 鎖標識
 * 多個執行緒搶此標識,誰搶到了就表示誰獲得了鎖或持有了鎖,就可以繼續執行,沒有搶到鎖標識的則被迫進入到阻塞狀態
 * ,等待持有鎖的執行緒釋放了鎖後再次加入到搶鎖標識的佇列中。
 */
class LockFlag {}

2、售票執行緒:

package com.joyupx.cms.example.globalLock5;

/**
 * 測試視窗賣票
 */
public class SellTicketThread {

    public static void main(String[] args) {
        /**
         * 建立售票任務物件
         */
        SellTicketTask6 sellTicketTask = new SellTicketTask6();

        /**
         * 建立 5 個執行緒,模擬 5 個視窗的工作。
         */
        /**
         * 模擬視窗一的工作
         */
        Thread sellTicketThread1 = new Thread(sellTicketTask, "視窗一");
        /**
         * 模擬視窗二的工作
         */
        Thread sellTicketThread2 = new Thread(sellTicketTask, "視窗二");
        /**
         * 模擬視窗三的工作
         */
        Thread sellTicketThread3 = new Thread(sellTicketTask, "視窗三");
        /**
         * 模擬視窗四的工作
         */
        Thread sellTicketThread4 = new Thread(sellTicketTask, "視窗四");
        /**
         * 模擬視窗五的工作
         */
        Thread sellTicketThread5 = new Thread(sellTicketTask, "視窗五");

        /**
         * 啟動執行緒,模擬視窗開始工作。
         * 只有透過啟動(start)方法才能觸發執行(run)方法。
         */
        /**
         * 啟動執行緒一,模擬視窗一開始工作。
         */
        sellTicketThread1.start();
        /**
         * 啟動執行緒二,模擬視窗二開始工作。
         */
        sellTicketThread2.start();
        /**
         * 啟動執行緒三,模擬視窗三開始工作。
         */
        sellTicketThread3.start();
        /**
         * 啟動執行緒四,模擬視窗四開始工作。
         */
        sellTicketThread4.start();
        /**
         * 啟動執行緒五,模擬視窗五開始工作。
         */
        sellTicketThread5.start();
    }

}

相關文章