文章目录
实验要求一、程序流程图(试验原理)二、变量说明三、功能分析及设计四、多线程卖票的实现五、实验结果及代码代码:实验要求
用多线程设计一个模拟火车站售票大厅的工作情形。火车站有许多售票窗口,有些开放,有些不开放。用多个线程去订票,不能有两个或者以上的线程订到同一个票,当最后一张票卖掉时,再订就异常提示出票卖完了。每个窗口买票需要1-3秒的时间,每次卖票需要打印出买票的时间和买票的窗口名。
一、程序流程图(试验原理)
二、变量说明
count:当前剩余总票数 place[]:途径站点
num:本次售卖总票数 number[]:相应起始地与目的地之间剩余票数
toplace[]:存储相应起始地与目的地信息
三、功能分析及设计
①火车站有许多窗口,有些开放有些不开放,我们规定一共10个售票口,每次随机开放3-10个,所以我们引入java.util.Random,java.util.Vector,java.util.Collections设计卖票前的vect方法,先得到一个3-10随机开放窗口数open,在创建一个存储随机数的集合,执行open次数的int number = r.nextInt(10) + 1,并利用!v.contains(number)去重得到当前开放窗口,用 Collections.sort(v)排序后显示当前开放窗口。
②用多个线程去订票,不能有两个或者以上的线程订到同一个票,当最后一张票卖掉时,再订就异常提示出票卖完了。每个窗口买票需要1-3秒的时间,每次卖票需要打印出买票的时间和买票的窗口名,所以我们引入java.util.Random,java.text.SimpleDateFormat,java.util.Calendar,用random模拟每次买票需要1-3秒的随机时间,再规定时间格式SimpleDateFormat formatter = new SimpleDateFormat (“yyyy-MM-dd HH:mm:ss”),并将对当前出票时间按设定格式输出formatter.format(calendar.getTime())。起始地与目的地用random随机抽取toplace[]信息,并减少number[]相对应票数。
多线程模拟售票窗口订票则需要重写类的run()方法,改为执行新的sellTicket()方法,实现线程互斥和线程同步,并通过改变线程状态以控制线程调度,每次卖票后当前剩余总票数count减1,用Trainticket t = new Trainticket(num)建立总票数对象,最后创建open数量的线程对象窗口,new Thread(t,“售票点”+v.elementAt(i)).start()建立并运行线程,模拟售票操作。
四、多线程卖票的实现
我们用重写的run()方法调用sellTicket()方法模拟多线程卖票:
①将方法声明为synchronized类型,synchronized是一种同步锁,他的作用是一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞,以保证线程间的互斥,即售票窗口不能同时办理卖票操作。
②进程的阻塞与唤醒,通过this.notifyAll(),this.wait()实现,为防止由于synch ronized锁定造成的单窗口处理所有卖票操作结果,我们将买卖票成功的窗口通过this.wait()休眠,则下一次卖票不会访问上一次的线程,再用this.notifyAll()唤醒所有睡眠线程继续工作直到总票数为0,进行一次票数等于0的判断帮助跳出循环,所有窗口显示票数为0并结束售票。
③模拟卖一张票的过程需要花1-3秒,Thread.sleep(rand.nextInt(2001)+ 1000),让线程休息售票时间模拟引票处理,此时程序休息等待处理结束。
④创建线程对象并开始卖票,用Trainticket t = new Trainticket(num)建立总票数对象,最后创建open数量的线程对象窗口,用for遍历建立并启动相应的线程new Thread(t,“售票点”+v.elementAt(i)).start()模拟售票操作。
每次卖票操作输出包括当前时间,当前线程名Thread.currentThread().getName(),剩余总票数count,以及出发地与目的地,卖完票时显示窗口票数卖完。
五、实验结果及代码
总票数:20张
天津------->北京:2张,北京------->天津:2张,天津------->沈阳:4张,
北京------->沈阳:5张,沈阳------->北京:1张,沈阳------->天津:6张。
总售票口数:10 随机开放[3,10]个售票口
第一次卖票:
第二次卖票:
代码:
import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Collections;import java.util.Random;import java.util.Vector;public class Trainticket implements Runnable{private int count;private String[] place = {"北京","天津","沈阳"};//站点private static int num=20;//总票数private static int []number={2,2,4,5,1,6};//相应起始地与目的地之间票数private static String[] toplace={"天津------->北京","北京------->天津","天津------->沈阳","北京------->沈阳","沈阳------->北京","沈阳------->天津",};SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public Trainticket(int t) {this.count = t;}@Overridepublic void run() {//重写run()方法this.sellTicket();}private synchronized void sellTicket(){while (this.count>0){this.notifyAll();Random rand = new Random();Calendar calendar = Calendar.getInstance();System.out.print(formatter.format(calendar.getTime())+"\t"+Thread.currentThread().getName()+"\t成功出票剩余票数:"+(--count));//输出当前的时间,售票口和票数Random r = new Random();int p=r.nextInt(6);while(number[p]==0){p=r.nextInt(6);}number[p]--;System.out.println("\t"+toplace[p]);if (this.count==0)break;try {Thread.sleep(rand.nextInt(2001)+ 1000); //模拟卖一张票的过程需要花1-3秒this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+"票已售完");}private static void vect(Vector<Integer> v,int m,int n){Random r = new Random();// 创建一个存储随机数的集合int open=r.nextInt(m)+n;int count = 0;while (count < open) {int number = r.nextInt(10) + 1;//一共10个窗口,开放指定数量// 判断number是否在集合中存在if (!v.contains(number)) {// 不在集合中,就添加v.add(number);count++;}}Collections.sort(v);System.out.print("开放的售票口为:");for(int i=0;i<v.size();i++){System.out.print(" 售票点"+v.elementAt(i)+" ");}System.out.println();}public static void main(String[] args) {// TODO Auto-generated method stubVector<Integer> v = new Vector<Integer>();vect(v,8,3);//开放3-10个窗口//创建线程类对象Trainticket t = new Trainticket(num);//一共20张票//启动线程for(int i=0;i<v.size();i++){new Thread(t,"售票点"+v.elementAt(i)).start();}}}