软件构造 Lab6

实验目的:
本次实验训练学生的并行编程的基本能力,特别是 Java 多线程编程的能力。 根据一个具体需求,开发两个版本的模拟器,仔细选择保证线程安全(threadsafe) 的构造策略并在代码中加以实现,通过实际数据模拟,测试程序是否是线程安全 的。另外,训练学生如何在 threadsafe 和性能之间寻求较优的折中,为此计算吞 吐率和公平性等性能指标,并做仿真实验。
⚫ Java 多线程编程
⚫ 面向线程安全的 ADT 设计策略选择、文档化
⚫ 模拟仿真实验与对比分析

实验需要的知识: Java 多线程编程、线程安全、各类线程上锁的方法、strategy设计模式。

实验任务:

1、ADT设计方案
ADT:
1、Monkey
作用:作为猴子类,生成线程
属性如下:
在这里插入图片描述
方法:
只有一个run函数,负责线程的工作。
在这里插入图片描述
2、Step
作用:踏板,继承自ReentrantLock,一次只能被一个线程(Monkey)占有。
属性:
Owner,存储着占有他的Monkey。
方法:
Owner getter/setter,获取Owner/设置Owner。

3、Ladder
作用:梯子类。
属性:
如下:
在这里插入图片描述
属性中存储着踏板、梯子ID和梯子上的猴子队列,猴子队列用于协助进行策略选择。
方法:
在这里插入图片描述
4、MonkeyFactory
作用:猴子工厂,用于生成猴子。
属性:无。
方法:
在这里插入图片描述
用于生成猴子。

5、MonkeyGenerator
作用:猴子生成器,继承自TimerTask,每隔一段时间生成一批猴子。
属性:
如下:
在这里插入图片描述
方法:
Run()
每隔一段时间生成一批猴子,如果猴子数量已经足够,就停止这个猴子生成器。

Constructor构造函数,构造一个猴子生成器。

6、MonkeyRiver
作用:模拟猴子过河的类,负责整个过程。
属性:MonkeyGenerator,猴子生成器。
方法:构造函数,构造一个猴子过河类。

7、GuiMain
作用:生成GUI,程序的主函数。
属性:
如下:
在这里插入图片描述
方法:
initialize(): 初始化GUI程序。
按钮1:按照输入内容开始生成猴子过河程序。
按钮2:按照文件内容生成猴子过河程序。(v1不需要,未完成,在v3版本可用)

2、Monkey线程的run()的执行流程图
在这里插入图片描述

3、至少两种“梯子选择”策略的设计与实现方案
策略1:
优先选择没有猴子的梯子,若所有梯子上都有猴子,则优先 选择没有与我对向而行的猴子的梯子;若满足该条件的梯子有很多, 则随机选择;
策略2:
优先选择没有猴子的梯子;若所有梯子上都有猴子,则优先选择没有与我对向而行的猴子,且最近一只猴子速度比我大,速度之差最小的梯子;如果没有速度比我大的梯子,就选择速度差最小的梯子。

实现方案:
选择空梯子:
遍历到一个梯子的时候需要锁住,防止同时也有猴子上梯子。
在这里插入图片描述
方案1:
在这里插入图片描述
先锁住梯子,从中抽出所有没有与我相向而行的猴子的梯子,然后随机选择其中之一上梯子。

方案2:
在这里插入图片描述
同样先锁住梯子,从中用一个ladder来记录最后要选择的梯子,在找到一个合适的梯子之后,和现有的梯子比较哪个梯子速度更快,选择更快的梯子保存到ladder。最后选择ladder即可。

4、“猴子生成器”MonkeyGenerator
首先,属性中有:
在这里插入图片描述
根据这两个参数,可以进行一轮轮的生成猴子。如下:
每轮生成一批猴子,如果中途达到上限,就停止生成。
在这里插入图片描述
MonkeyGenerator是一个TimerTask,于是我在MonkeyRiver中,设置一个Timer来每隔一段时间就调用一次这个任务。如下:
在这里插入图片描述
这样就可以完成猴子生成器的任务。

5、如何确保threadsafe
首先,在每一个猴子选择梯子的时候,都要锁住这个梯子,防止同时上梯,也就防止了猴子相向而行造成死锁。
其次,每一个Ladder的Step都是ReentrantLock,这样就可以保证每个位置都只会有一个猴子。猴子在登上这个位置的时候lock,在离开的时候unlock。经过一个梯子的时候,按顺序lock,unlock路径上的每一个Step,遇到已经有Owner的Step,就停下脚步不再前进,等下一秒的动作。

6、系统吞吐率和公平性的度量方案
度量方案参考实验指导书:
在这里插入图片描述
实现:
吞吐率:在所有猴子到达对面以后,计算总耗时,然后用猴子数量除以总耗时即可。
公平性:在计算总耗时时,每只猴子与其他猴子比较,然后根据实验指导书内容,计算即可。

代码:
在这里插入图片描述

7、输出方案设计
日志
在每一只猴子完成一个动作后,都输出一个log,记录到日志中。猴子可可以访问logger和日志地址。效果如图:
在这里插入图片描述
在这里插入图片描述
GUI
设置两个按钮,分别对应两个输入方式。
一个JList和JScrollPane来显示猴子状态。并且设置一个Timer,每隔一段时间监听猴子状态并刷新JList内容。所有猴子过河后,终止监听Timer。
在这里插入图片描述
所有猴子过河后,弹窗显示吞吐率和公平性。
在这里插入图片描述

8、猴子过河模拟器v1
参数初始化
在这里插入图片描述
v1版本的猴子过河只能从GUI中输入参数,进行初始化。共六个参数。

使用Strategy模式为每只猴子选择决策策略
在这里插入图片描述
每只猴子在生成时,使用随机数,随机选择一个决策策略。
之后,再根据决策策略,来选择梯子。
如:
在这里插入图片描述

9、猴子过河模拟器v2
在不同参数设置和不同“梯子选择”模式下的“吞吐率”和“公平性”实验结果及其对比分析。

对比分析:固定其他参数,选择不同的决策策略
参数:n=5, h=20,mv=10,k=20,t=1,N=100
结果:
在这里插入图片描述
可以看出,策略1的公平性要比策略2高许多,因为策略1每个猴子在生成后可以马上选择一个梯子,比较少的出现后来的猴子比先前的猴子早到的情况。
吞吐率策略2也高于策略1,因为策略2更重视梯子整体速度,较少出现速度被限制浪费的情况。

对比分析:变化某个参数,固定其他参数
这里以n为例。
参数:h = 10, mv = 10, k = 50, t = 1, N = 100
n为10,15,20,25,30
在这里插入图片描述
由上表可以看出,吞吐率随着n的变大而变大,公平性则变化不是很大。

分析:吞吐率是否与各参数/决策策略有相关性?
有决策相关,决策不同,吞吐率就会不同,比如策略2的吞吐率就比策略1的吞吐率要高。与参数也相关,例如n改变时,吞吐率也会发生改变。

压力测试结果与分析
参数:n=10,h = 10, mv = 10, k = 500, t = 1, N = 500
结果:吞吐率为0.037,公平性为0.288。

参数:n=10,h=50,mv=50,k=50,t=1,N=1
结果:吞吐率为0.390,公平性为-0.009。

从压力测试的结果中可以看到,压力测试对于公平性的影响并不是很大,主要的影响在于吞吐率。
在梯子有限而猴子数量大且密集时,吞吐率和数量大但分散的情况并没有太大区别,但这个实验结果原因在于猴子很快就通过了梯子,并没有互相限制速度,所以吞吐率变化不大。如果降低猴子速度,则变化会较大。同时,猴子数量大且密集,会导致吞吐率比数量小而分散要低。
在猴子速度差距较大时,和猴子速度差距区别不大的情况没有太大区别,因为在策略选择时,就考虑到了各猴子速度互相限制的问题。我们的猴子会选择比较少的限制路径来走,所以影响不大。

10、猴子过河模拟器v3
模拟结果如下:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值