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