BUAA_OO 第二单元作业-电梯调度
一、同步块的设置和锁
我的设计并没有完全实现类的封装,使用的类并非完全线程安全。在WaitingQueue中,我尝试对WaitingQueue的take和put操作进行上锁。然而实际上由于我采用了CopyOnWriteArrayList,这是完全不必要的。这也是在逐渐摸索中发现的。
private final CopyOnWriteArrayList<PersonRequest> personRequests;
public void addPersonRequest(PersonRequest p) {
try {
personRequests.add(p);
this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
public PersonRequest take() {
while (personRequests.isEmpty()) {
try {
personRequests.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return personRequests.remove(0);
}
例如以上代码,其中同步块关键词synchronized是没有必要的,但是我在初次设计的时候由于对线程安全类不熟悉,所以并没有写出很漂亮的代码。
那么实际上,要向personRequests添加或者删除请求,都是可以直接采用CopyOnWriteArrayList提供的接口的。这也提示我们,如果自己要设计一个线程安全类,一定要在内部实现同步,而不是给代码编写者。这样符合面向对象的思维。
同步块的实现是为了防止在两个线程读取同一块内存时发生冲突。例如: Thread A 试图读取 class MyClass 内容,而同时 Thread B 试图修改 Class MyClass 内容,那么 Thread A 既可能读取B修改前的内容,也可能读取 Thread B 修改后的内容,导致的访问的不稳定。我们采用 synchronized 关键字,可以在某一个线程访问时拒绝其他线程访问对应对象,有效避免混乱的发生。
需要注意的是,如果某一线程同步访问某一地址,然而由于种种原因无法获取对象的锁,会陷入“占着茅坑不拉屎”的尴尬局面,持续占用处理器资源而不做事。因此我们采用 wait 和 notify 的方式在恰当的时机放弃处理器资源的占用。
二、调度器设计
考虑到三种电梯的运行效率问题,我们将依次分配C、B、A类型电梯,如果分配成功则返回true,若失败则返回false。
判断是否分配一辆电梯的原则:
- 若方向相同且乘客出发楼层在电梯运行途径中,则捎带乘客。
- 若电梯停止且有乘客发出请求,则无条件接待乘客。
private boolean allocateElevator() {
PersonRequest personRequest = waitingQueue.take();
ProcessingInteraction best = null;
int from = personRequest.getFromFloor();
int to = personRequest.getToFloor();
boolean flag = allocateTypeC(personRequest);
if (flag) {
return true;
}
flag = allocateTypeB(personRequest);
if (flag) {
return true;
}
for (ProcessingInteraction item : processingInteractions) {
if (item.getElevatorType().equals("A") && !item.isFull()) {
if (item.getStateOfElevator() == StateOfElevator.DOWN && to < from
|| item.getStateOfElevator() == StateOfElevator.UP && to > from
|| item.getStateOfElevator() == StateOfElevator.WAIT) {
best = item;
}
}
}
if (best == null) {
waitingQueue.addPersonRequest(personRequest);
return false;
}
best.addNewRequest(personRequest);
return true;
}
三、架构设计
设计思路
MainClass:在主类中初始化时间戳,并且调用控制台输入类。
Input:控制输入,初始化3个电梯并运行;初始化调度器并运行。
Scheduler:调度器类。根据输入指令将乘客请求指派电梯或者电梯请求增加电梯。
Elevator:电梯类。使用look算法,根据乘客请求运送。
WaitingQueue:等待分配队列。输入的乘客请求加入到其中,等待scheduler分配电梯。
ProcessingInteraction:处理队列。调度器将乘客分配给某个电梯,等待电梯处理。





四、BUG与DEBUG
本次作业由于对于多线程的运行机制不清晰,前期主要bug来自于同步块的设置问题,不敢说时bug,只能说是一窍不通。我的第一次和第二次作业都没有完成。在第三次作业前,我终于理解了同步和锁机制,那么剩下的bug其实很简单。我对提供的数据点进行逐个调试(print方式),查询是否有死循环和调度错误,逐个修复。由于不会按时间戳输入代码,我采用了评论区的python代码,由于答题设计思路正确,只需逐步调试即可。
五、体会
第二单元作业是对于Java多线程的应用,模拟一个生活实际问题。本单元作业的难点(对我而言)在于理解并发机制。但是由于时间受限、基础知识不理解等原因导致我前两次作业均为能完成。实际上我仍然每周花至少20小时来学习这门课程,但是无奈没有好的教程和合适的辅导者,以至于我遇到问题甚至不知道如何解决。后来我经历了十分艰难的请教和学习,问过很多朋友才最终解决了锁机制的理解问题,继而做出了作业。
由于没有前两次作业的结果,我没有任何重复设计,也没有改进,谈不上优化。
总而言之,希望接下来努力学习,按时完成作业。
浙公网安备 33010602011771号