Java 多线程售票示例

 

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();
    }

}

 

posted @ 2024-11-03 13:09  hapday  阅读(7)  评论(0编辑  收藏  举报