计算机科学与技术专业课程设计任务书
题 目 | 司机与售票员 | ||
课题性质 | 其它 | 课题来源 | 自拟课题 |
主要内容 | 设计程序模拟在公共汽车上,司机和售票员操作的同步。 司机:启动车辆,正常行车,到站停车。 售票员:上乘客,关车门,售票,开车门,下乘客。 用PV操作对其控制。 | ||
任务要求 |
掌握信号量通信方式的一般方法,了解系统实现“阻塞”和“唤醒”功能的方法和技巧。同时掌握进程同步和互斥的概念及实现技术。 | ||
参考文献 | [1]孟静.计算机操作系统原理教程.北京:清华大学出版社,2005 [2]广树建,新编C/C++程序设计教程.广州:华南理工出版社,2008 [3]汤子瀛,哲凤屏.计算机操作系统.北京:电子工业出版社,2009 [4]尤晋元,史美林等.windows操作系统原理.北京:机械工业出版社,2001 [5]郁红英,李春强.计算机操作系统.北京:清华大学出版社,2008 [6]庞丽萍,阳富民.计算机操作系统(第四版) .北京:人民邮电出版社,2006 | ||
审查意见 | 指导教师签字: 教研室主任签字: 年 月 日 |
说明:本表由指导教师填写,由教研室主任审核后下达给选题学生,装订在设计(论文)首页
目 录
题目的主要内容为设计程序模拟在公共汽车上,司机和售票员操作的同步。司机:启动车辆,正常行车,到站停车。售票员:上乘客,关车门,售票,开车门,下乘客。用PV操作对其控制。
题目的要求为掌握信号量通信方式的一般方法,了解系统实现“阻塞”和“唤醒”功能的方法和技巧。同时掌握进程同步和互斥的概念及实现技术。
该程序是用Java语言进行开发设计的,其中建立了一个主类class DriverAndSeller,然后在其中建立了两个内部类class Driver和class Seller,其中设计了三个函数,分别是Driver类的run( )函数,Seller类的run( )函数,以及主函数,如表2-1。
表 2-1 概要设计表
类 | 函数名 | 参数 | 功能 |
Driver | run( ) | 无 | 实现司机的进程 |
Seller | run( ) | 无 | 实现售票员的进程 |
DriverAndSeller | main( ) | 无 | 实现司机与售票员进程的同步 |
具体的PV操作如图2-1所示,司机和售票员的进程同步。
图2-1 司机和售票员的进程同步
硬件环境: PC机内存:8G 硬盘:256G
软件环境: 操作系统:windows10
开发工具:IntelliJ IDEA Community Edition 2021.3.2
编程语言:Java
DriverAndSeller类是Java程序的主类,含有两个内部类class Driver和class Seller。其中含有两个信号量:司机信号量driverNotify和售票员信号量sellerNotify。两个信号量同步使用,可以实现司机与售票员的进程同步。
其中含有主函数main( )函数,建立一个DriverAndSeller类的对象,以及两个进程ExecutorService es = Executors.newFixedThreadPool(2);,分别为司机进程es.execute(das.new Driver());和售票员进程es.execute(das.new Seller());。
源程序代码为:
public static void main(String[] args){
DriverAndSeller das = new DriverAndSeller();
ExecutorService es = Executors.newFixedThreadPool(2);
es.execute(das.new Driver());
es.execute(das.new Seller());
es.shutdown();
return;
}
5.2 Driver类
Driver类为DriverAndSeller类的内部类,其中含有一个run( )函数,具体内部运行如图5-1所示。
类内含有Random对象,用来模拟不同站点上下车所需要的不同时间,以及行车的不同时间;try-catch函数,来捕获异常,保证进程的正常运行;synchronized( )修饰代码块即指定加锁对象,对给定对象/类加锁。synchronized(类.class) 表示进入同步代码前要获得 当前 class 的锁,当该类的锁解除后才能进行下面的活动;Thread.sleep( )休眠函数,即让该进程等待随机的时间以便进行其他进程的活动;pork与total_pork的比较,用来判断是否到达最终站点,以便相应的对象进行相应的活动。
图5-1 Driver类的流程图
在该类的进程执行的过程中司机先申请信号量,等待到后进行启动车辆,进程休眠,售票员的进程可以进行相应的活动如售票,然后休眠结束,司机正常行行车,休眠一段时间即司机的行车时间,然后到达新站点,判断下是否为最终站点,司机到站停车,释放售票员的信号量,售票员可以进行相应的活动即开车门、下乘客。其中有try-catch语句保障程序的正常执行,如果出现了错误,会显示出相应的异常,e.printStackTrace();。
源程序代码为:
class Driver implements Runnable{
public void run(){
Random random = new Random(47);
for(;pork<total_pork;){
try{
synchronized (driver) {
if(driverNotify==0)
driver.wait();
driverNotify--;
}
System.out.println("[司 机]: 启动车辆...");
sleep(random.nextInt(1000)+1000);
System.out.println("[司 机]: 正常行车...");
sleep(random.nextInt(6000));
pork++;
System.out.println("[到达新站点]第"+pork+"站...");
if(pork==total_pork){
System.out.println("[司 机]:终点站...");
}
System.out.println("[司 机]: 到站停车...");
synchronized (seller) {
sellerNotify++;
seller.notify();
}
sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5.3 Seller类
Seller类为DriverAndSeller类的内部类,其中含有一个run( )函数,具体内部运行如图5-2所示。
类内含有Random对象,用来模拟不同站点上下车所需要的不同时间,以及行车的不同时间;try-catch函数,来捕获异常,保证进程的正常运行;synchronized( )修饰代码块即指定加锁对象,对给定对象/类加锁。synchronized(类.class) 表示进入同步代码前要获得 当前 class 的锁,当该类的锁解除后才能进行下面的活动;Thread.sleep( )休眠函数,即让该进程等待随机的时间以便进行其他进程的活动;pork与total_pork的比较,用来判断是否到达最终站点,以便相应的对象进行相应的活动。
图5-2 Seller类的流程图
在该类的进程执行的过程中首先判断是否为第一站,如果是则执行第一站的操作,公交车开始,第一站。售票员执行上乘客,关车门的行为,释放司机的信号量并进行休眠,司机进行相应的操作即启动车辆,售票员休眠结束,执行售票,售票员等待司机进程释放信号量,即在司机正常行车到达新站点,执行完到站停车后,司机释放售票员的信号量,售票员执行开车门、下乘客,然后循环执行上乘客、关车门,依次执行。其中有try-catch语句保障程序的正常执行,如果出现了错误,会显示出相应的异常,e.printStackTrace();。
源程序代码为:
class Seller implements Runnable{
public void run(){
Random random = new Random(47);
for(;pork<total_pork;) {
try {
if (pork == 1)
System.out.println("[公交车开始]第1站...");
System.out.println("[售票员]: 上乘客...");
get_on_num=random.nextInt(total_num-recent_num);
recent_num+=get_on_num;
System.out.println("上乘客"+get_on_num+"人");
System.out.println("目前车内总人数为"+recent_num);
System.out.println("[售票员]: 关车门...");
synchronized (driver) {
driverNotify++;
driver.notify();
}
sleep(random.nextInt(1000) + 500);
System.out.println("[售票员]: 售票...");
sleep(random.nextInt(1000) + 5000);
synchronized (seller) {
if (sellerNotify == 0)
seller.wait();
sellerNotify--;
}
sleep(random.nextInt(500) + 300);
System.out.println("[售票员]: 开车门...");
System.out.println("[售票员]: 下乘客...");
get_off_num=random.nextInt(recent_num);
recent_num-=get_off_num;
if (pork == total_pork) {
recent_num=0;
System.out.println("全部乘客下车");
System.out.println("车内人数"+recent_num);
return;
}
System.out.println("下乘客"+get_off_num+"人");
System.out.println("目前车内总人数为"+recent_num);
sleep(random.nextInt(800) + 300);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
6调试分析
- 容易存在死锁,因为如果seller执行完V(Driver)之后Driver还没有执行到P(Driver),那么当Driver执行到P(Driver)之后则会挂起,而Seller到了P(Seller)之后也同样会挂起,因此死锁产生。
- 没有设置站点很容易看错,也对司机和售票员之间的具体进程没有太实际的了解。
- 没有写休眠时间进程容易冲突,休眠时间一样也会冲突
- 没有加代码块锁
- 死锁产生的原因是V操作的信号被忽略了。因此只要保持住该信号就可以唤醒Driver。
- 设置站点,从第一站开始售票员和司机的进程依次执行,最终站点会有显示,可以给观测者更加贴近实际的感觉。
- 加上休眠时间可以更贴近实际的运行过程,而且在有休眠时间的情况下,进程之间会以一种很合理的方式运行下去,不会出现进程抢占、死锁的情况。
- 加上代码块锁可以使进程进行合理,以及信号量申请,释放也更加逻辑化。
- 释放司机信号量
synchronized (driver) {
driverNotify++;
driver.notify();
}
- 设置站点
int total_pork=4;
int pork=1;
进行判断:
if(pork==1)
System.out.println("[公交车开始]第1站...");
System.out.println("[售票员]: 上乘客...");
System.out.println("[售票员]: 关车门...");
synchronized (driver) {
driverNotify++;
driver.notify();
}
if(pork==total_pork){
System.out.println("[司 机]:终点站...");
}
- 释放司机信号量,可以使司机只是在等待不会破坏其他进程的顺序性。
synchronized (driver) {
driverNotify++;
driver.notify();
}
申请售票员信号量,可以使售票员等待司机进程执行完毕,逻辑清晰,结构紧凑。
synchronized (seller) {
if(sellerNotify==0)
seller.wait();
sellerNotify--;
}
测试结果图,如图7-1
图7-1 测试结果图
- 张丽芬,刘美华.操作系统原理教程.北京:电子工业出版社,2004.
- 倪继利.Linux内核分析及编程.北京:电子工业出版社,2005.
- 张尧学,史美林.计算机操作系统教程.2版.北京:清华大学出版社,2000.
- 基于Linux安全漏洞渗透测试方法的研究[J]. 金涛,李新剑,刘宏斌,史震宇. 网络空间安全. 2019(03).
- 周婷,董海棠.Eclipse平台架构及其插件[J].甘肃科技纵横,2007(03):16+151.
- 周宝亮,王敏.计算机软件开发中JAVA编程语言的应用[J].电子技术与软件工程,2017(03):61-62.
计算机操作系统是铺设在计算机硬件上的多层系统软件,不仅增强了系统的功能,而且还隐藏了对硬件操作的细节,由它实现了对计算机硬件操作的抽象。通过此次课设,让我对计算机操作系统的一些实现原理和简单的操作过程有了基本的了解。
经过这一段时间的课程设计,我去了解了很多关于线程的知识,一开始以为线程的结束是无法自发停止的,后来发现了需要调用线程自带的函数shutdown()从而帮助关闭线程,这也是和之前写的Java程序不太相同的地方。在此次设计中遇到过一些问题,很幸运地是在同学的帮助下和自己对网上资料的研究,成功完成了此次课设。