情景描述
火车站卖火车票
需求分析
火车站很多人同时过来买票(高并发),这里假设:车站只卖一天的同一辆列车的同一种票,一共500张,座位号从1——500;
1、如何保证两个顾客不会买到同一张票;
2、有多个售票窗口(多个售票员);
3、售票系统只有一个(一个票池);
4、每个售票员都需要记录自己接待的客户数量;
5、如何在高并发访问的情况下,保证自己记录的接待数不会存在线程安全问题;
具体代码
/**
* @author GY
* @date 2019年7月7日
* @说明: 人类
*/
public class Person {
/**
* 姓名
*/
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name) {
super();
this.name = name;
}
}
/**
* @author GY
* @date 2019年7月7日
* @说明: 火车票
*/
public class Ticket {
/**
* 座位号
*/
private int seatNumber;
public Ticket(int seatNumber) {
this.seatNumber = seatNumber;
}
public int getSeatNumber() {
return seatNumber;
}
public void setSeatNumber(int seatNumber) {
this.seatNumber = seatNumber;
}
@Override
public String toString() {
return "[已出票,您的车票座位号是:" + seatNumber + "]";
}
}
/**
* @author GY
* @date 2019年7月7日
* @说明: 火车票系统
*/
public class TicketSystem {
/**
* 票务系统中拥有的所有票
*/
private static List<Ticket> tickets = new ArrayList<>();
static {
for (int i = 1; i <= 500; i++) {
// 初始化500张票
tickets.add(new Ticket(i));
}
}
/**
* @author GY
* @date 2019年7月7日
* @说明: 出票
*/
public static Ticket pop() {
if(tickets.size() < 1) {
return null;
}
Ticket ticket = tickets.get(0);
tickets.remove(0);
return ticket;
}
}
/**
* @author GY
* @date 2019年7月7日
* @说明: 售票员
*/
public class Seller {
/**
* 该销售员已售票数量
*/
private int receiveNum = 0;
public int getReceiveNum() {
return receiveNum;
}
public void setReceiveNum(int receiveNum) {
this.receiveNum = receiveNum;
}
/**
* @author GY
* @date 2019年7月7日
* @说明: 买票
*/
public void sellTicket(Person person) {
System.out.println("客户咨询,当前售票员接待客户数+1");
this.addReceiveNum();
System.out.println("打开售票系统,出票");
Ticket pop = TicketSystem.pop();
if (pop == null) {
System.out.println("【【已售罄】】");
} else {
System.out.println("打印火车票:" + person.getName() + pop);
}
}
/**
* @author GY
* @date 2019年7月7日
* @说明: 增加接待数
*/
public void addReceiveNum() {
int newNum = receiveNum + 1;
try {
// 这里为了体现并发不安全效果而特意将线程睡眠1毫米
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
receiveNum = newNum;
}
}
/**
* @author GY
* @date 2019年7月7日
* @说明: 售票事件
*/
public class SellerSellTicket implements Runnable {
/**
* 顾客
*/
private Person person;
/**
* 售票员
*/
private Seller seller;
public SellerSellTicket(Person person, Seller seller) {
this.person = person;
this.seller = seller;
}
@Override
public void run() {
seller.sellTicket(person);
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
// 创建一个线程池来处理并发
ThreadPoolExecutor pool = new ThreadPoolExecutor(6, 12, 3, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), new ThreadPoolExecutor.CallerRunsPolicy());
Seller seller_1 = new Seller();
//模拟500个顾客同时过来买票
for (int i = 1; i <= 500; i++) {
pool.execute(new SellerSellTicket(new Person("客户_" + i), seller_1));
}
TimeUnit.SECONDS.sleep(2);// 下班
System.out.println("下班统计:seller_1 接待客户数为:" + seller_1.getReceiveNum());
}
}
可知如上是不安全的线程访问,如下修改
TicketSystem中 类锁运用
/**
* @author GY
* @date 2019年7月7日
* @说明: 出票
*/
public synchronized static Ticket pop() {
if(tickets.size() < 1) {
return null;
}
Ticket ticket = tickets.get(0);
tickets.remove(0);
return ticket;
}
Seller中 对象锁运用
/**
* @author GY
* @date 2019年7月7日
* @说明: 买票
*/
public synchronized void sellTicket(Person person) {
System.out.println("客户咨询,当前售票员接待客户数+1");
this.addReceiveNum();
System.out.println("打开售票系统,出票");
Ticket pop = TicketSystem.pop();
if (pop == null) {
System.out.println("【【已售罄】】");
} else {
System.out.println("打印火车票:" + person.getName() + pop);
}
}