使用多线程编程模拟车票销售

也许大家会有这样的经历,我们在车站排队买回家的车票。如果某一条线路是比较热门的线路,通常车站会同时开几个窗口供大家排队购买这一线路的车票,当然,如果某几条线路是比较冷门的话,也可能是在一个窗口就可以分别买这几条不同线路的车票。那么,如果以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<Destination, Ticket> tickets;

    /**
     * 静态代码块,初始化集合对象,并添加各目的地车票映射
     */
    static {
        tickets = new HashMap<Destination, Ticket>();
        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<Destination, Integer> details;

    /**
     * 初始化代售窗口对象信息
     * 
     * @param id
     *            代售窗口编号
     */
    public BookingOffice(Integer id) {
        super();
        this.id = id;
        tickets = 0;
        details = new HashMap<Destination, Integer>();
    }

    @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}

当然,各线程运行的顺序可能与以上结果显示不一致

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值