301
社区成员
发帖
与我相关
我的任务
分享说是第二单元,其实是五、六两次作业,第七次没写完所以寄了。
因为上面的原因,以hw6来举例说明我的实现的整体架构。
PS C:\some\random\path\to\src> tree /F .
.
Controller.java
Elevator.java
ElevatorQueue.java
ElevatorState.java
Input.java
MyPersonRequest.java
Program.java
Queue.java
Scheduler.java
我设计的类包括:
将电梯和控制器分离属于预测错误导致的设计失误。这样设计原本是为了适应像往年的”横向电梯“场景,在”双轿厢电梯“场景这样的设计反倒增加了复杂性。

我在hw5使用了同步块,hw6和hw7换成手动加锁,避免了搞笑的缩进。
hw5时由于我对同步块的机制不够了解,给Controller的run()方法的主循环加了同步块,导致无法捎带,造成了很严重的性能问题。
注意ElevatorQueue和Queue两个类,在hw6和hw7中,我将加解锁的逻辑全部移动到这两个类中,很大地降低了心智负担。
随机策略。很敷衍,但是当你发现随机策略比自己“精心”设计的调度策略表现更好的时候,保持理智是一件很难的事情。
从性能分上看,随机策略是比较差的,但不是最差的调度策略。
至于耗电量,也许我们可以发布一份调查问卷,调查究竟有多少人在意过这个耗电量。
Input类
只需要单方面接受输入即可。两个线程共同持有Queue<Request>的引用。
Controller类
Scheduler将一个请求加入Controller的队列,并notify这个队列Controller将队列中的请求加入Scheduler的队列采用了指导书推荐的ALS策略,中规中矩。
稳定的:主类,输入,队列,调度策略,电梯运行策略
易变的:电梯本身
我手动实现了一个类似互斥锁的结构,让两个轿厢控制器持有该结构的引用。
当一个轿厢试图进入换乘层时,它先检查锁是否被占用,若是,它notify另一个线程并等待。它成功进入换乘层时先获取锁,离开后释放锁。
此外,我规定当轿厢进入换乘层之后,需要尽快离开,即使当前不再有请求。
给双轿厢电梯只分配一个线程也可以防止冲突,但会导致令人担忧的性能问题
notify()wait()导致的多线程读写,容器类没加锁导致的ConcurrentModificationException
分配策略不合理
很多人在hw6都受到了这则数据的洗礼:
[1.0]100-FROM-1-TO-11
[10.0]RESET-Elevator-6-3-0.6
[49.0]RESET-Elevator-1-3-0.3
[49.0]RESET-Elevator-2-3-0.3
[49.0]RESET-Elevator-3-3-0.3
[49.0]RESET-Elevator-4-3-0.3
[49.0]RESET-Elevator-5-3-0.3
[49.9]10-FROM-10-TO-11
[49.9]70-FROM-9-TO-11
...
因为其余电梯都在重置,不好的调度策略会导致乘客在6号电梯门口踩踏,场面一度非常混乱
我自己也中招了
对于死锁问题,我使用调试功能查看死锁线程的调用栈,从而定位死锁原因
对于错误的分配策略,有两种解决方案:一种是设置当有过多数量的电梯重置时暂停分配,另一种是为电梯增加buffer。
我选择了前者,因为可以压到五行以内 :
import java.util.stream.IntStream;
if (IntStream.rangeClosed(1, elevatorCount)
.filter(i -> getController(i).isResetPendingOrResetting()).count() >= 4) {
requestQueue.await();
}
线程安全:一句话概括: 涉及多线程读写要加锁
层次化设计:实话讲对本单元我在这一点上做得很烂,原因开头讲了
第七次没写完所以寄了。
将电梯和控制器分离属于预测错误导致的设计失误。
但是做没做呢?“将电梯和控制器分离”也是一种层次化,只是设计方向与需求不匹配,反而拖了后腿。
如何平衡代码设计、未来可能的增量和模块化额外消耗的精力,是一个值得思考的问题。
花了四周时间,在多线程这块算是入了门,正巧OS那边也学到多进程、多线程的内容,相信这会让我对多线程程序设计与更深的理解()