oo第二单元总结
第一次作业
由于没有通过中测,故不献丑了
第二次作业
同步块的设置和锁的选择
-
共享对象
由图可知,building对象被输入线程,电梯线程,调度器线程共享;commingqueue对象被输入线程和调度器线程共享。
-
同步块设置
-
输入线程
-
电梯线程
每次访问building时加锁。对于电梯的终止条件,没有通过commingqueue来实现,而是在调度器里面对building设置标志,电梯访问该标志终止。
电梯没有任务时,等待调度器分配任务给processqueue。
-
调度器线程
这里只有一个输入源,故按照逻辑先看commingqueue是否为空,若为空在看输入是否结束,若结束则唤醒所有等待调度的电梯线程,否则调度器等待输入,将commingqueue释放。因此先锁commingqueue再锁building。
但正因为这样的逻辑,导致无法支持换乘的二次调度,因为换乘请求可能还未加入commingqueue,而输入已经结束且commingqueue为空导致电梯提前终止。因此下一次作业我采取假换乘,即不把请求加给commingqueue,而是直接加到processqueue中。
其实还有一种解法,将电梯监视器也被调度器共享,增加调度终止逻辑可以实现,但同时会导致共享对象的增多。
-
调度器设计
调度器访问commingqueue,把其中的请求按照一定策略分发给building中的processqueue,并通过commingqueue的end标志和empty标志给电梯的processqueue的end标志置位表示调度结束。这里的策略是分给当前最懒的电梯以使得所有电梯都能够运行起来。
Bug
-
debug
本次作业模仿实验课代码的模板写成,公测没有遇到什么bug,强侧也没有被hack。
-
hack
hack时尝试在输入一堆数据后就输入null,测试有无睡死过去的进程。
思考与总结
-
性能分析
-
分析
既支持调度器调度电梯,也支持电梯之间互相抢夺指令。
本次作业调度器使得所有电梯能够动起来,没有特别的调度策略,也没有动态根据电梯情况调度。
电梯采取不完全自由竞争,即电梯只能够在运行主请求的过程中可以去抢夺请求和捎带已经被调度的请求。这样不完全符合实际,性能上略微损失。
-
改进
可以去除每一个电梯自己的运行队列和调度器,电梯没有指令时仅仅去向公共的请求队列要一个指令,如此更加符合实际且消除电梯抢夺请求时维护processqueue的花销。
-
-
线程安全
commingqueue不需要被电梯共享,通过building这一共享对象实现scheduler和elevator之间的通信。降低线程之间共享对象的数量。
-
层次化设计
-
分析
电梯监视器这个类很合适,将电梯的人的请求单独管理,降低耦合度。
难以支持换乘的需求,因为elevator无法访问commingqueue。因为共享对象存在耦合,粒度太大,难以支持更加细致的功能。
-
改进
-
方案一
完全自由竞争,即集中式设计。去除processqueue,building作为commingqueue的容器,去除调度器。
-
方案二
降低抢夺的自由度,损失性能,每个电梯只能抢夺和捎带自己的请求,即分布式设计。building作为commingqueue的容器供调度器使用,每个电梯情况被调度器共享,方便调度。电梯线程共享commingqueue,以便结束。
-
-
第三次作业
同步块的设置和锁的选择
-
共享对象
同第二次作业,building对象被输入线程,电梯线程,调度器线程共享;commingqueue对象被输入线程和调度器线程共享。
-
同步块设置
-
输入线程
输入运行模式时锁住building,这里事实上不需要唤醒building,因为没有building的等待。
输入结束时给commingqueue的end置位,来一个请求就锁住commingqueue并加入,来一个电梯请求就锁住building并加入。
-
调度器线程
加锁情况和第二次作业一致。
-
电梯线程
与第二次作业的区别在于电梯的终止条件是当前电梯内没有人且所有电梯的processqueue都为空且调度结束才停止运行,因为存在换乘的可能电梯无法根据自己的processqueue为空且调度结束就停止运行。
其实主要是由于本人架构设计的模糊不清导致的结构混乱,理论上换乘是要把请求再次加入commingqueue进行二次调度,但这里由于共享对象耦合,采用当请求出电梯时直接将请求加入processqueue的方式模拟二次调度。
-
调度器设计
调度器的调度策略是按照起始楼层进行分类,分成三类:1-3,4-17,18-20。具体策略参见代码,值得一提的是最后有一个是否分配给A的调度判断以防止B、C太过繁忙,而A无事可做。
private String mayGiveItToA(String type) {
String newtype = type;
if (type.equals("C")) {
synchronized (this.building) {
if (this.building.getNumOfProcessInType("C") >
2 * this.building.getNumOfProcessInType("A")) {
newtype = "A";
}
}
}
else if (type.equals("B")) {
synchronized (this.building) {
if (this.building.getNumOfProcessInType("B") * 2 >
3 * this.building.getNumOfProcessInType("A")) {
newtype = "A";
}
}
}
return newtype;
}
public static String selectFromLowFloor(int from, int to) {
String type;
if ((to >= 1 && to <= 3) || (to >= 18 && to <= 20)) {
type = "C"; //C直送
}
else {
if ((from == 3) || (from == 1)) {
type = "B";
}
else {
type = "C";
}
}
return type;
}
public static String selectFromHighFloor(int from, int to) {
String type;
if ((to >= 1 && to <= 3) || (to >= 18 && to <= 20)) {
type = "C"; //C直送
}
else {
if (from == 18) {
type = "A";
}
else {
type = "C";
}
}
return type;
}
public static String selectFromMiddleFloor(int from, int to) {
String type;
if (from % 2 == 1) {
if (to % 2 == 1) {
type = "B"; //奇数到奇数
}
else {
if ((to == from