也许大家会有这样的经历,我们在车站排队买回家的车票。如果某一条线路是比较热门的线路,通常车站会同时开几个窗口供大家排队购买这一线路的车票,当然,如果某几条线路是比较冷门的话,也可能是在一个窗口就可以分别买这几条不同线路的车票。那么,如果以Java面向对象来设计的话,如何实现模拟这类似的售票功能呢?
首先,我们来建立一个枚举类型,模拟线路目的地
/**
* 目的地枚举
* @author 小明
*
*/
public enum Destination {
BEIJING, GUANGZHOU, XIAN, MIANYANG
}
接下来,我们来设计车票类
/**
* 火车票
*
* @author 小明
*
*/
public class Ticket {
private final Destination destination; // 目的地
private int current; // 当前剩余票数
private final int original; // 车票基数
/**
* 初始化目的地、车票基数
*
* @param destination
* 目的地
* @param oraginal
* 车票基数
*/
public Ticket(Destination destination, int original) {
super();
this.destination = destination;
this.original = original;
this.current = original;
}
public Destination getDestination() {
return destination;
}
public int getCurrent() {
return current;
}
public int getOriginal() {
return original;
}
/**
* 出售车票,则车票张数减少
*
* @return 剩余车票张数
*/
public int decrease() {
return --this.current;
}
/**
* 判断车票是否售完
*
* @return 车票是否售完,true:售完
*/
public boolean isSoldOut() {
return this.current <= 0;
}
@Override
public String toString() {
return "Ticket [destination=" + destination + ", current=" + current
+ ", original=" + original + "]";
}
}
车票类比较简单,对各属性仅提供了getter来访问该属性值
我们再设计一个类,主要用于数据存放,即放置整个系统中所使用到的车票资源信息,该信息会被所有售票窗口所共享
import java.util.HashMap;
import java.util.Map;
/**
* 初始化所有售票点都可以销售的车票信息
*
* @author 小明
*
*/
public class Data {
/**
* 以Map集合的方式,保存所有可供出售的车票信息
* key -- 目的地
* value -- 到达该目的地的车票对象
*/
public static Map tickets;
/**
* 静态代码块,初始化集合对象,并添加各目的地车票映射
*/
static {
tickets = new HashMap();
tickets.put(Destination.BEIJING, new Ticket(Destination.BEIJING, 10));
tickets.put(Destination.GUANGZHOU, new Ticket(Destination.GUANGZHOU, 5));
tickets.put(Destination.XIAN, new Ticket(Destination.XIAN, 20));
tickets.put(Destination.MIANYANG, new Ticket(Destination.MIANYANG, 8));
}
}
然后,我们开始设计销售窗口的类。这是一个在并发状态下对共享资源的多线程访问问题,所以该类继承了Thread类(当然也可以实现Runnable接口),重写父类run()方法,实现对共享资源的操作。
import java.util.HashMap;
import java.util.Map;
/**
* 代售窗口类
*
* @author 小明
*
*/
public class BookingOffice extends Thread {
// 代售窗口编号
private Integer id;
// 总销售的车票数量
private Integer tickets;
// 代售窗口出售车票的详情 key--目的地名 value--车票销售数量
private Map details;
/**
* 初始化代售窗口对象信息
*
* @param id
* 代售窗口编号
*/
public BookingOffice(Integer id) {
super();
this.id = id;
tickets = 0;
details = new HashMap();
}
@Override
public String toString() {
return "[BookingOffice-id-" + id + "]";
}
@Override
public void run() {
int cnt = 3;
while (cnt-- > 0) {
// 获取目的地
Destination[] destinations = Destination.values();
Destination destination = destinations[(int) (Math.random() * destinations.length)];
System.out.println(this + "to : " + destination);
// 暂停
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取目的地的车票对象
Ticket ticket = Data.tickets.get(destination);
// 标记是否出售一张车票
boolean flag = false;
synchronized (ticket) {
if (!ticket.isSoldOut()) { // 未出售完该趟车票
System.out.println(this + "" + destination + " tickets : "
+ ticket.getCurrent());
System.out.println(this + "" + destination + " print...");
int remain = ticket.decrease(); // 减去出售的火车票数
System.out.println(this + "" + destination + " remains : "
+ remain);
flag = true;
} else {
System.out.println(this + "" + destination + " sold out!");
}
}
/* 更新当前代售窗口出售车票的信息 */
if (flag) {
this.tickets++; // 总票数
// 详情
Integer count = details.get(destination);// 到目的地的所销售的票数
details.put(destination, count == null ? 1 : count + 1);
}
}
System.out.println(this + "total tickets : " + tickets + ", details : "
+ details);
}
}
因为我们所需要共享的资源是车票,当有人买了车票后我们需要修改车票资源信息,所以当获取到达某目的地的车票对象后,应该将其放入同步代码块中锁定,以达到各线程对该资源的访问是互斥的目的。
最后,我们在main方法中测试
/**
* 测试类
*
* @author 小明
*
*/
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new BookingOffice(i + 1).start();
}
}
}
结果:
[BookingOffice-id-1]to : BEIJING [BookingOffice-id-3]to : MIANYANG [BookingOffice-id-2]to : MIANYANG [BookingOffice-id-2]MIANYANG tickets : 8 [BookingOffice-id-2]MIANYANG print... [BookingOffice-id-2]MIANYANG remains : 7 [BookingOffice-id-2]to : BEIJING [BookingOffice-id-3]MIANYANG tickets : 7 [BookingOffice-id-1]BEIJING tickets : 10 [BookingOffice-id-3]MIANYANG print... [BookingOffice-id-3]MIANYANG remains : 6 [BookingOffice-id-3]to : BEIJING [BookingOffice-id-1]BEIJING print... [BookingOffice-id-1]BEIJING remains : 9 [BookingOffice-id-1]to : GUANGZHOU [BookingOffice-id-2]BEIJING tickets : 9 [BookingOffice-id-2]BEIJING print... [BookingOffice-id-2]BEIJING remains : 8 [BookingOffice-id-2]to : BEIJING [BookingOffice-id-3]BEIJING tickets : 8 [BookingOffice-id-3]BEIJING print... [BookingOffice-id-3]BEIJING remains : 7 [BookingOffice-id-3]to : BEIJING [BookingOffice-id-1]GUANGZHOU tickets : 5 [BookingOffice-id-1]GUANGZHOU print... [BookingOffice-id-1]GUANGZHOU remains : 4 [BookingOffice-id-1]to : BEIJING [BookingOffice-id-2]BEIJING tickets : 7 [BookingOffice-id-2]BEIJING print... [BookingOffice-id-2]BEIJING remains : 6 [BookingOffice-id-2]total tickets : 3, details : {MIANYANG=1, BEIJING=2} [BookingOffice-id-3]BEIJING tickets : 6 [BookingOffice-id-3]BEIJING print... [BookingOffice-id-3]BEIJING remains : 5 [BookingOffice-id-3]total tickets : 3, details : {MIANYANG=1, BEIJING=2} [BookingOffice-id-1]BEIJING tickets : 5 [BookingOffice-id-1]BEIJING print... [BookingOffice-id-1]BEIJING remains : 4 [BookingOffice-id-1]total tickets : 3, details : {GUANGZHOU=1, BEIJING=2}
当然,各线程运行的顺序可能与以上结果显示不一致