301
社区成员
发帖
与我相关
我的任务
分享
本次作业中,我借鉴了OO上机时的代码架构,设计了3个线程类和1个共享类。
三个线程类分别为输入线程 InputThread 、调度线程 Schdeuler 、电梯线程 Elevator ,一个共享类为 RequesQueue 运行过程中, Main 开启全部的线程(包括 1 个InputThread、1 个Schdeuler 、6 个 Elevator),并定义了 6 个共享对象RequesQueue。
本次作业指定电梯,不需要考虑人员的分配问题。而对于电梯的运行策略,我采用的是LOOK策略。这好像没什么好多说的。
线程间的通信都是通过共享对象来进行的,即InputThread与Scheduler共享一个侯乘表,请求到时就由InputThread放入侯乘表,同时notifyAll()唤醒等待着的Scheduler,Scheduler将请求从侯乘表中取走。Scheduler与Elevator间的通信也是如此。线程图如下


我加入了随机神教。嘿嘿。
又由于我让吐出去的乘客由原先的电梯接受,所以线程图没有基本没变。


本次新增了三个类,分别是:
单例模式的Counter类,用来判断进程结束条件
共享对象的Flag类,用来确保双轿厢电梯不同时进入换乘层
线程DoubleElevator类,用来满足双轿厢电梯需求
public class Counter {
private static Counter instance = null;
private int cnt;
public synchronized void release() {
cnt++;
notifyAll();
}
public synchronized void acquire() {
while (true) {
if (cnt > 0) {
cnt--;
break;
}
else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static Counter getInstance() {
if (instance == null) {
instance = new Counter();
}
return instance;
}
}
public class Flag {
enum State {
OCCUPIED,
UNOCCUPIED
}
private State state;
public Flag() {
this.state = State.UNOCCUPIED;
}
public synchronized void setOccupied() {
waitRelease();
state = State.OCCUPIED;
notify();
}
public synchronized void setRelease() {
this.state = State.UNOCCUPIED;
notify();
}
private synchronized void waitRelease() {
notify();
while (state == State.OCCUPIED) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

由于架构的简单直接,这三次迭代我还是完成的比较顺利,期间也没有遇到多线程场景下容易发生的线程安全问题。但这种粗暴的设计也导致我损失了一定的性能分。还是挺令我感到遗憾的。
但总结看来,Random大法在可拓展性极高的优势。同时,它不需要调度器知晓电梯自身的状态,避免了线程间的过多耦合以及这可能导致的线程不安全问题。在客户需求没那么严苛的情境下,这不失为一种好的选择。
在第七次作业中,我由于未考虑到两个双轿厢共享侯乘表时,双方的修改都会导致另一方可能被唤醒。假设有A电梯不需要移动,但是B电梯收到了大量乘客需要移动,这就会导致A电梯被不断地唤醒,产生一定程度上的轮询。最终这导致我CTLE。不过由于这并不会轮询太多次,我在后面加了个sleep就应付了bug修复。
至于多线程debug方面,轮询的问题可以通过print来找到,但其他bug我感觉就需要通过数据生成与比对来处理了,我也是并没有发现什么特别有效的方法。