Java多线程之铁路售票系统攻略
一、需求分析
铁路售票系统需要满足的主要需求:
- 售票窗口可以同时售卖多张票,需要支持并发访问。
- 售票系统需要保证售卖的票数不能超过存库量。
- 当售票系统资源被其他线程占用时,需要等待其他线程执行完毕后才能获取资源。
二、设计思路
根据需求,我们可以采用以下设计思路:
- 定义 Ticket 类表示火车票,其中包括车次、出发时间、座位号等信息。
- 定义 TicketDatabase 类表示火车票库存,其中包括每种车次的剩余票数、已售票数等信息。
- 定义两个线程类:SellerThread 类表示售票窗口的线程,它们可以并发访问售票系统;CheckerThread 类表示检票员线程,用于检票。
- 在 SellerThread 线程中实现售票逻辑,包括从 TicketDatabase 中获取库存、判断是否售出已售光、实现售票等操作。
- 在 CheckerThread 线程中实现检票逻辑,包括检票、验证车票有效性等操作。
三、代码实现
1. Ticket 类
public class Ticket {
private String trainNo; // 车次
private String departureTime; // 出发时间
private String seatNo; // 座位号
public Ticket(String trainNo, String departureTime, String seatNo) {
this.trainNo = trainNo;
this.departureTime = departureTime;
this.seatNo = seatNo;
}
public String getTrainNo() {
return trainNo;
}
public String getDepartureTime() {
return departureTime;
}
public String getSeatNo() {
return seatNo;
}
}
2. TicketDatabase 类
public class TicketDatabase {
private final Map<String, Integer> ticketStock = new ConcurrentHashMap<>();
public TicketDatabase(List<String> trains) {
for (String train : trains) {
ticketStock.put(train, 100); // 初始库存为 100 张
}
}
public boolean bookTicket(String trainNo) {
int stock = getStock(trainNo);
if (stock > 0) {
ticketStock.put(trainNo, stock - 1);
return true;
}
return false;
}
public int getStock(String trainNo) {
return ticketStock.getOrDefault(trainNo, 0);
}
}
3. SellerThread 类
public class SellerThread extends Thread {
private final TicketDatabase ticketDatabase;
public SellerThread(TicketDatabase ticketDatabase) {
this.ticketDatabase = ticketDatabase;
}
@Override
public void run() {
while (true) {
synchronized (ticketDatabase) {
if (ticketDatabase.getStock("G1001") == 0) {
break; // 如果该车次已售光,则结束售票
}
if (ticketDatabase.bookTicket("G1001")) {
System.out.println(Thread.currentThread().getName() + ": " + "售票成功!");
} else {
System.out.println(Thread.currentThread().getName() + ": " + "售票失败!");
}
}
}
}
}
4. CheckerThread 类
public class CheckerThread extends Thread {
private final TicketDatabase ticketDatabase;
public CheckerThread(TicketDatabase ticketDatabase) {
this.ticketDatabase = ticketDatabase;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(50); // 等待 50ms
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (ticketDatabase) {
System.out.println("检票员检票: " + ticketDatabase.getStock("G1001"));
}
}
}
}
5. 示例说明
以下是两条示例说明:
示例一
我们创建两个售票窗口线程和一个检票员线程,同时设置库存为 101 张。预期输出结果如下:
售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
检票员检票: 91
检票员检票: 89
检票员检票: 87
检票员检票: 85
检票员检票: 83
检票员检票: 81
检票员检票: 79
检票员检票: 77
检票员检票: 75
检票员检票: 73
检票员检票: 71
检票员检票: 69
检票员检票: 67
检票员检票: 65
检票员检票: 63
检票员检票: 61
检票员检票: 59
检票员检票: 57
检票员检票: 55
检票员检票: 53
检票员检票: 51
检票员检票: 49
检票员检票: 47
检票员检票: 45
检票员检票: 43
检票员检票: 41
检票员检票: 39
检票员检票: 37
检票员检票: 35
检票员检票: 33
检票员检票: 31
检票员检票: 29
检票员检票: 27
检票员检票: 25
检票员检票: 23
检票员检票: 21
检票员检票: 19
检票员检票: 17
检票员检票: 15
检票员检票: 13
检票员检票: 11
检票员检票: 9
检票员检票: 7
检票员检票: 5
检票员检票: 3
检票员检票: 1
代码如下:
public static void main(String[] args) {
List<String> trains = Arrays.asList("G1001");
TicketDatabase ticketDatabase = new TicketDatabase(trains);
SellerThread seller1= new SellerThread(ticketDatabase);
SellerThread seller2 = new SellerThread(ticketDatabase);
CheckerThread checker = new CheckerThread(ticketDatabase);
seller1.start();
seller2.start();
checker.start();
}
示例二
我们创建三个售票窗口线程和一个检票员线程,同时设置库存为 2 张。预期输出结果如下:
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口3: 售票失败!
售票窗口3: 售票失败!
检票员检票: 0
代码如下:
public static void main(String[] args) {
List<String> trains = Arrays.asList("G1001");
TicketDatabase ticketDatabase = new TicketDatabase(trains);
SellerThread seller1= new SellerThread(ticketDatabase);
SellerThread seller2 = new SellerThread(ticketDatabase);
SellerThread seller3 = new SellerThread(ticketDatabase);
CheckerThread checker = new CheckerThread(ticketDatabase);
seller1.start();
seller2.start();
seller3.start();
checker.start();
}
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java多线程之铁路售票系统 - Python技术站