大家好,欢迎来到IT知识分享网。
目录
一、 设计要求 1
二、 运行环境 1
三、 系统功能 1
四、 数据结构 2
五、 算法流程图 3
六、 主要功能的代码 4
1. 初始化链表 4
2. 生产者进程 4
3. 消费者进程 6
七、 运行结果截图 7
1. 开始界面——初始化 7
2. 选择公共缓冲池最大缓冲区数量 8
3. 开始运行 9
4. 演示过程 10
正常运行 10
缓冲池已满 11
缓冲池为空 12
结束 13
5. 帮助界面 14
八、 小结 14
设计要求
设计一个模拟仿真“生产者-消费者”问题的解决过程及方法的程序。
主要内容是P、V操作过程的设计与实现。生产消费者问题是操作系统设计中经常遇到的问题。多个生产者和消费者线程访问在共享内存中的环形缓冲。生产者生产产品并将它放入环形缓冲,同时消费者从缓冲中取出产品并消费。当缓冲区满时生产者阻塞并且当缓冲区有空时生产者又重新工作。类似的,消费者当缓冲区空时阻塞并且当缓冲区有产品时又重新工作。显然,生产者和消费者需要一种同步机制以协调它们的工作。
运行环境
本软件运行在PC 及其兼容机上,已安装Java虚拟机且JDK版本为jdK 1.8 及以上版本。
系统功能
本程序模拟实现了“生产者-消费者”问题的解决过程,用图形界面动态演示了P、V操作过程以及生产者、消费者进程之间的工作流程。
本程序使用的算法是典型的P、V操作使用信号量解决“生产者-消费者”问题。
本程序在界面上使用了Java的swing接口函数,用矩形条表示生产者进程中待生产的产品,并设置了三个分区分别表示生产者进程待生产的产品、公共缓冲池中已生产的产品和消费者进程已消费的产品,以动画的效果动态演示了待生产产品变成消费者进程中已消费产品的过程,以及在这一过程中生产者进程和消费者进程协调工作的过程。在程序运行过程中使用了两个生产者线程和两个消费者线程并发工作,并使用了线程随机休眠的策略,即每个线程在完成一次生产过程或消费过程后随机休眠1至10秒钟。这一策略能保证生产者和消费者之间的运行顺序被打破,从而产生生产产品和消费产品之间的矛盾(即没有产品可消费的情况下消费者试图向公共缓冲池取产品消费、公共缓冲池里的产品已满的情况下生产者试图生产产品放入缓冲池)。因为生产者生产产品和消费者消费产品都是随机的,所以产生的矛盾也是不可预知的,在这种情况下,才能检验所使用的算法是否健壮高效。而本程序正是基于这种思想设计出来的,用来模拟生产者消费者问题的解决过程。
本程序在运行时提供友好的交互界面,且操作简单,在模拟过程中各种情况有相应文字提示,并伴有相应的图像变化,如:当没有产品可消费的情况下消费者试图向公共缓冲池取产品消费,消费者进程阻塞,公共缓冲池随之变成红色,文字提示框内显示warning: it’s empty!Consumer is block;当缓冲池已满而生产者试图生产产品并向缓冲池放入产品时,生产者进程阻塞,公共缓冲池里的每一个产品变成黄色,问题提示框显示warning: it’s full!Producer is block。整个模拟过程通俗易懂,利于理解,能很好的帮助使用者加强生产者消费者问题的理解。
数据结构
本程序使用链表LinkedList<String>表示生产者要生产的产品、公共缓冲池里的产品和消费者已消费的产品,链表里的一个节点元素表示一个产品。
算法流程图
图 1算法流程图
主要功能的代码
初始化链表
/**
* @Fields proList : 生产者待生产产品链表
*/
/**
* @Fields conList : 消费者已消费产品链表
*/
/**
* @Fields comList : 公共缓冲池链表
*/
public static LinkedList<String> proList,conList,comList;
/**
* @Fields full : 公共缓冲池是否已满
*/
public static boolean full=false;
/**
* @Fields empty : 公共缓冲池是否为空
*/
public static boolean empty=false;
//初始化链表
static {
proList=new LinkedList<>();
conList=new LinkedList<>();
comList=new LinkedList<>();
}
生产者进程
class Producer extends Thread {
/* (non-Javadoc)
* Title: run
* Description: 重写run方法
* @see java.lang.Thread#run()
*/
@Override
public void run() {
while (getNumbers()>=1) {
//加锁
lock.lock();
try {
//线程停止2秒是为了让程序走的慢一些,不至于一眨眼就结束了
sleep(2000);
if (MyFrame.comList.size() == getMAX()) {
//若公共缓冲池已满,将当前进程加入等待队列
//直到被唤醒:full.signal
//当前进程进入等待队列
full.await();
}
String str = MyFrame.proList.removeLast();
if (MyFrame.comList.add(str)) {
//若object对象成功添加进队列comList
//说明生产者进程成功生产一个产品并送入公共缓冲池
//唤醒等待的消费者进程
empty.signal();
}
} catch (InterruptedException ie) {
//捕获到异常说明进程被异常中断
util.showInfo(“producer is interrupted!\n”);
} finally {
//次数减一
lessNumber();
//释放锁
lock.unlock();
try {
//线程随机暂停几秒是为了让生产者和消费者之间的运行产生冲突
//从而达到演示的目的
sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
消费者进程
class Consumer extends Thread {
/* (non-Javadoc)
* Title: run
* Description: 重写run方法
* @see java.lang.Thread#run()
*/
@Override
public void run() {
while (!(getNumbers()==0 && MyFrame.comList.size()==0)) {
//加锁
lock.lock();
try {
//线程停止2秒是为了让程序走的慢一些,不至于一眨眼就结束了
sleep(2000);
if (MyFrame.comList.size() == 0) {
//若comList长度为0说明公共缓冲池里没有没有缓冲区可用
//即缓冲区为空,没有产品可取
//将当前进程放入等待队列直到被唤醒:empty.signal()
empty.await();
}
//消费者进程成功将产品从缓冲池取出
String str = MyFrame.comList.removeLast();
if(MyFrame.conList.add(str)) {
//唤醒在等待的生产者进程
full.signal();
}
} catch (InterruptedException ie) {
util.showInfo(“consumer is interrupted!\n”);
} finally {
//释放锁
lock.unlock();
try {
//线程随机暂停几秒是为了让生产者和消费者之间的运行产生冲突
//从而达到演示的目的
sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
util.showInfo(“结束”);
}
}
}
运行结果截图
开始界面——初始化
图 2开始界面
选择公共缓冲池最大缓冲区数量
图 3选中最大缓冲区数量
开始运行
图 4开始运行
演示过程
正常运行
图 5正常运行
缓冲池已满
图 6缓冲池已满
缓冲池为空
图 7缓冲池为空
结束
图 8模拟结束
帮助界面
图 9帮助界面
小结
大概浏览了一遍课程设计任务书,发现里面大部分题目所使用的算法并不是很复杂,而且在教材中均有提及,但如何用程序仿真模拟,其工作量和难度就大小不一了。我选择“生产者-消费者”问题这个题目的原因主要是时间不充裕,若选择工作量大的题目,恐怕在提交日期前无法完成,而且这个题目实现起来并不难,工作量中等,所以选中了“生产者-消费者”问题这个题目作为课设的题目。
在算法上我采用了比较典型的P、V操作+整形信号量的算法,该算法在教材上有详细解释,在此就不做过多的说明。
我认为,“生产者-消费者”问题的模拟仿真用程序实现主要有两个难点:一是如何用图形来实时动态演示生产者生产产品、消费者消费产品的每一个过程,以及发生阻塞时如何表示;另一个难点是如何使生产者和消费者之间的运行产生随机性,使整个运行是不可预知的,以此来检验算法的健壮有效性。
这里先说第二个难点的解决方法。正常情况下,程序的运行次序是固定不变的,假设是生产者先运行,之后的顺序在没有任何中断异常的情况下应该是“生产者将产品放入缓冲池——消费者从缓冲池取出产品——生产者将产品放入缓冲池”一直循环直到结束,在这种情况下几乎是不可能出现缓冲池无产品可取或缓冲池已满的情况的,那么模拟仿真 也就没有意义了。为了解决这个问题,我使用了“每个线程执行了一次生产或消费后随机等待(不是阻塞)1至10秒”的策略,从而保证了两者的随机性,很好的解决了这个问题。
第一个难点的解决方法是,使用三个链表proList,comList,conList分别表示生产者待生产产品、公共缓冲池产品和消费者已消费产品。当生产者生产一个产品并放入缓冲池时,链表对应的变化是proList的最后一个节点元素移出并加进comList;当消费者从缓冲池取出一个产品并消费时,链表对应的变化是comList的最后一个节点元素移出并加进conList。三个链表每有一个节点就画出一个矩形条,链表每变化一次,页面就刷新一次,从而达到实时动态演示的效果。
这次课程设计,使我加深了对生产者消费者问题的理解和系统底层的线程之间的调度的理解,同时让我对Java的多线程有了更深刻的认识,对我日后的编程有很大的帮助。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/83164.html