实验要求
用多线程设计一个模拟火车站售票大厅的工作情形。火车站有许多售票窗口,有些开放,有些不开放。用多个线程去订票,不能有两个或者以上的线程订到同一个票,当最后一张票卖掉时,再订就异常提示出票卖完了。每个窗口买票需要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;
}
@Override
public 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 stub
Vector<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();
}
}
}