多线程练习程序 之 模拟火车票售票系统01版

程序源码:
http://pan.baidu.com/s/1pKF5vpL

模拟火车票售票系统用来练习多线程再好不过了.
这是第01版,尽量简化难度,我们在之后的版本,一步一步完善.
本节,我们将学习构建一个线程安全且高效的简易版火车票售票系统

题目描述

为了简化数据,我们只考虑以下的火车站点:
这里写图片描述
模拟列车表如下:

列车     始发点=时间   抵达点=时间   抵达点=时间
D011    北京=00:30    上海=06:10
D012    北京=02:00    上海=08:00
D013    北京=04:30    上海=10:20
D014    北京=08:00    上海=14:00
D015    北京=11:25    上海=18:40
D016    北京=15:00    上海=21:00
D017    北京=17:00    上海=22:40
D018    北京=23:00    上海=次日05:20
G011    北京=07:45    长沙=14:40
G012    北京=00:10    长沙=08:00    广州=10:30
G013    北京=07:15    长沙=14:40    广州=17:10
G014    北京=10:45    长沙=18:40    广州=20:55
G015    北京=13:00    长沙=20:40    广州=23:00
G016    北京=21:30    长沙=次日05:20  广州=次日08:00
G017    北京=22:35    长沙=次日06:25  广州=次日10:00
D021    上海=01:00    北京=07:15
D022    上海=03:35    北京=10:15
D023    上海=06:30    北京=12:30
D024    上海=08:30    北京=14:30
D025    上海=10:50    北京=16:00
D026    上海=15:00    北京=21:10
D027    上海=19:00    北京=23:50
D028    上海=23:30    北京=次日06:00
G021    上海=23:30    长沙=次日07:00
G122    上海=01:20    长沙=10:05    广州=12:35
G123    上海=08:10    长沙=16:45    广州=19:15
G124    上海=11:30    长沙=19:25    广州=21:50
G125    上海=14:10    长沙=22:40    广州=次日01:05
G126    上海=20:25    长沙=次日05:00  广州=次日07:30
G127    上海=22:00    长沙=次日07:00  广州=次日09:35
G031    长沙=21:40    北京=05:30
G032    长沙=11:40    上海=20:30
D031    长沙=06:30    广州=08:55
D031    长沙=08:30    广州=11:00
D031    长沙=13:00    广州=15:30
D031    长沙=18:40    广州=21:00
G042    广州=00:20    长沙=02:45    北京=10:35
G043    广州=07:10    长沙=09:40    北京=17:15
G044    广州=10:30    长沙=12:55    北京=20:50
G045    广州=13:10    长沙=14:40    北京=23:05
G046    广州=21:25    长沙=23:50    北京=次日07:55
G047    广州=22:30    长沙=次日00:55  北京=次日10:10
D041    广州=06:40    长沙=09:00
D041    广州=09:40    长沙=12:00
D041    广州=14:00    长沙=13:40
D041    广州=19:40    长沙=22:20
G142    广州=01:20    长沙=03:45    上海=12:35
G143    广州=08:10    长沙=10:40    上海=19:15
G144    广州=11:30    长沙=13:55    上海=21:50
G145    广州=14:10    长沙=16:40    上海=次日01:05
G146    广州=20:25    长沙=22:50    上海=次日07:30
G147    广州=22:00    长沙=次日00:35  上海=次日09:35

在01版系统中,我们只考虑以下需求:

1.购票: 提供购票操作,每有一次购票操作,票数能正确的减一,并且支持并发购票
2.退票: 支持退票操作,每有一次退票操作,票数正确的加一,并且支持并发退票

设计思路

这里写图片描述

–>系统启动后,将开启后台线程,线程池
–>后台任务不断获取任务队列中的任务,将任务交给线程池
–>线程池执行任务,涉及到火车票库的操作,交给火车票库处理
–>线程池分析最终结果,将交给out发出(01版暂不考虑,所以只将信息存入了out字符串)

类图结构:

这里写图片描述

核心代码

源代码下载:
http://pan.baidu.com/s/1pKF5vpL

TicketSystem火车票库

//列车列表
private final Map<Train,Train> list;
//票库
private final Map<Ticket,AtomicInteger> tickets;
//票库镜像
private final Map<Ticket,AtomicInteger> mirror;

//买票
boolean buyTicket(Ticket t){
    AtomicInteger i = mirror.getOrDefault(t, null);
    if(i!=null){
        if(i.decrementAndGet()>=0)
            return true;
        i.incrementAndGet();
    }
    return false;
}

//退票
boolean cancelTicket(Ticket t){
    AtomicInteger i = mirror.getOrDefault(t, null);
    if(i==null)
        return false;
    i.incrementAndGet();
    return true;
}

//放票
public boolean addTicket(Ticket t,int num){
    AtomicInteger i = mirror.getOrDefault(t, null);
    if(i==null)
        return false;
    synchronized (i) {
        i.set(i.get()+num);
    }
    return true;
}

SellTicketSystem 售票系统

//票库
private final TicketSystem ts;
//执行任务线程池
private final ExecutorService server;
//任务队列
private final BlockingQueue<Channel> tasks;
//后台线程
private final Thread backThread;
//构造系统
private SellTicketSystem(String path,int num,Date date){
    num = num>0&&num<1<<6?num:DEFUALT_THREAD;
    this.ts = TicketSystem.create(path,date);
    server = Executors.newFixedThreadPool(num);
    tasks = new LinkedBlockingQueue<>(num<<3);
    backThread = new Thread(){
        public void run() {
            while(!isInterrupted()){
                try{
                    server.submit(tasks.take());
                }catch(InterruptedException e){
                    System.err.println("获取Channel被中断");
                    break;
                }
            }
            System.err.println("获取Channel已经停止运行");
        }
    };
}   
//启动
public synchronized static boolean start(String path,int num,Date date){
    if(sys==null){
        sys = new SellTicketSystem(path,num,date);
        sys.backThread.start();
        System.err.println("系统安全启动");
        return true;
    }else{
        System.err.println("警告: 系统已经启动,请勿重复启动!");
        return false;
    }   
}
//关闭
public synchronized static boolean shutdown(){
    sys.server.shutdown();
    sys.backThread.interrupt();
    return true;
}

测试

在test.txt中提前写入所有测试的指令

buy G017    2017-06-10  北京  广州
buy G017    2017-06-10  北京  广州
buy G017    2017-06-10  北京  广州
buy G017    2017-06-10  北京  广州
cancel  G017    2017-06-10  北京  广州
buy G017    2017-06-10  北京  广州
buy G017    2017-06-10  北京  广州
cancel  G017    2017-06-10  北京  广州
buy G017    2017-06-10  北京  广州
buy G016    2017-06-10  北京  广州
buy G016    2017-06-10  北京  广州
......

有三个测试类
1.串行测试类,这个类用于测试串行执行指令的结果是否正确,并将所有结果存储在result.txt中
主要代码:

List<String> list = FileUtils.readLines(new File(path));
List<Channel> clist = new ArrayList<Channel>(); 
for (String op : list) {
    Channel c = new Channel(op);
    c.run();            
    clist.add(c);
}
StringBuilder sb = new StringBuilder();
clist.forEach(e->sb.append(e).append("\r\n"));
FileUtils.write(new File(newPath), sb.toString());

2.单线程测试类, 测试线程池执行指令结果是否正确,并将结果存储在singleThreadTest.txt中
核心代码:

List<String> list = FileUtils.readLines(new File(path));
List<Channel> clist = new ArrayList<Channel>(); 
for (String op : list) {
    Channel c = new Channel(op);
    SellTicketSystem.sys().addTask(c);
    clist.add(c);
}
Thread.sleep(200);
StringBuilder sb = new StringBuilder();
clist.forEach(e->sb.append(e).append("\r\n"));
FileUtils.write(new File(newPath), sb.toString());

3.对比两个测试结果,相同的行说明执行结果正确,不相同的行,需要人工去检查线程池的执行结果.

这里写图片描述

多线程执行结果是否正确是非常难以判断的,采用上面的方式能减轻不少的负担.
最终,测试这个系统是比较健壮的了,那么01版的模拟火车票售票系统也就大功告成了.

源码:
http://pan.baidu.com/s/1pKF5vpL

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值