实验目标:Multi-Thread Concurrent Programming
本次实验训练学生的并行编程的基本能力,特别是Java多线程编程的能力。根据一个具体需求,开发两个版本的模拟器,仔细选择保证线程安全(threadsafe)的构造策略并在代码中加以实现,通过实际数据模拟,测试程序是否是线程安全的。另外,训练学生如何在threadsafe和性能之间寻求较优的折中,为此计算吞吐率和公平性等性能指标,并做仿真实验。
⚫ Java多线程编程
⚫ 面向线程安全的ADT设计策略选择、文档化
⚫ 模拟仿真实验与对比分析
这次实验,难度偏大,debug过程极其复杂,好在代码量不是很大。
第一部分:ADT设计
ADT的设计,主要是猴子,梯子和猴子生成器的设计。
对于猴子,没什么特殊的,就是序号,速度,方向。
move方法:猴子在梯子上移动,需要考虑猴子到达对岸和猴子被阻挡两种情况,所以只能一步一步走。
public synchronized int move(String direction, int position, int velocity) {
int dest = position;
if (direction.equals("L->R")) {
for (int i = 1; i <= velocity; i++) {
if (position + i > h) {
dest = 0;
break;
} else if (!steps.containsKey(position + i)) {
break;
} else {
dest++;
}
}
} else {
for (int i = 1; i <= velocity; i++) {
if (position - i <= 0) {
dest = 0;
break;
} else if (!steps.containsKey(position - i)) {
break;
} else {
dest--;
}
}
}
if (dest != 0) {
steps.put(dest, steps.get(position));
}
if (dest != position) {
steps.remove(position);
}
return dest;
}
randomSpawnMonkey是随机分批生成猴子的方法,可以作为工厂方法使用。
public void randomSpawnMonkey() {
int num = 0;
Random random = new Random();
String direction = "R->L";
for (int i = 0; i < Main.N / Main.k; i++) {
for (int j = 0; j < Main.k; j++) {
if (random.nextInt(2) == 0) {
direction = "L->R";
}
int velocity = random.nextInt(Main.MV) + 1;
Monkey monkey = new Monkey(num, direction, velocity);
num++;
monkey.start();
}
try {
Thread.sleep(Main.t * 1000);
} catch (Exception e) {
}
}
for (int i = 0; i < Main.N % Main.k; i++) {
if (random.nextInt(2) == 0) {
direction = "L->R";
}
int velocity = random.nextInt(Main.MV) + 1;
Monkey monkey = new Monkey(num, direction, velocity);
num++;
monkey.start();
}
}
第二部分:策略设计
采用Strategy设计模式,在两种策略之间自由切换。
public static Strategy newStrategy(String choice) {
if (choice.equals("Fast")) {
return new Fast();
} else if (choice.equals("Empty")) {
return new Empty();
} else {
throw new RuntimeException();
}
}
在策略的选择上,我选择了两种比较容易编写和实现的策略:
(1)优先选择整体推进速度最快的梯子
(2)先选择没有猴子的梯子,若所有梯子上都有猴子,则在岸边等待,直到某个梯子空闲出来。
具体实现不在此赘述。
第三部分:确保线程安全
1.使用private,限制其他类对数据的访问
2.设计Immutable的ADT
3.使用线程安全的mutable类型
4.使用同步的机制
这些都是保证线程安全的重要方法,在多线程程序中应用的十分广泛。如果我能在刚开始设计的时候尽多做到这些,就不会花费那么多时间去debug了。
第四部分:度量与日志
这部分,日志没什么可说的,在以前的实验里边已经操作过了,这次就是照葫芦画瓢。
对于吞吐率和公平性的度量,按照公式计算就可以了。
在分析各个变量对吞吐率和公平性的影响时,可以用图表的形式来直观的看,然后再从理论上分析。
结果:
1.吞吐率与策略有关。上面的分析指出,Fast策略中,多个猴子可以排队前进,而Empty策略只能是一个猴子独占一个梯子。所以Fast策略一定不比Empty策略吞吐率低。实验也支持这一点。
2.吞吐率与各参数的关系:吞吐率与N, MV, k, n正相关,与t负相关。