301
社区成员
发帖
与我相关
我的任务
分享
InputThread - Schedule(Thread) - ElevatorThread 的三类线程InputThread - Schedule 之间采用生产者 - 消费者模型,共用一个 waitQueue 作为请求总池。Schedule根据乘客的指派的电梯进行分配由于RequestTable类在多个线程中使用,因此对这个类的任何读写操作都加了锁。此外,由于对RequestTable的任何读写操作我都在该类内完成,我的所有synchronized块全部而且仅出现在请求类中。具体来说,读写RequestTable类主要分为以下几种情况:
addPerson: 两类请求都要用,用于向请求队列中加入新的请求(Person)。popAndRemovePerson: 从主请求中pop出一个Person用于分配给电梯子请求。isEnd和其他一些队列情况的方法,也需要上锁。这样我的读写全部封装在类内进行,外面只需要调用这些函数即可。并且我在RequestTbale中专门写了wait和notifyall方法,方便外面的类调用。

整体介绍:
InputThread - Schedule(Thread) - ElevatorThread 的三类线程InputThread - Schedule 之间采用生产者 - 消费者模型,共用一个 waitQueue 作为请求总池。waitQueue进行分配,所以我的ElevatorThread也相当于一个生产者Scheduler 中,Elevator 负责存储有关的电梯物理信息,如容量、速度、方向等),各个线程各司其职。advice.reset()Reset()Out方法,out People的队列中。synchronized (mainTable) {
TimableOutput.println(String.format("RESET_BEGIN-%d",this.id));
Thread.sleep(1200);
TimableOutput.println(String.format("RESET_END-%d", this.id));
}
requestTable中的请求清空到needAlloc队列中outPeople队列,将请求重新加回电梯的requestTable,只加回了reset后的容量个数的请求,其余的扔回总池子needAlloc中的请求扔回总池子中flag,cnt置为0由于我的Reset方法中,会将请求扔回总池重新分配,所以我线程的结束就不能沿用第五次的结束方法——以inputThread输入的结束时间处为各个电梯的设置endflag
RequestCounter采用单例模式,在InputThread线程和Elevator被调用
InputThread
@Override
public void run() {
try {
int personNum = 0;
ElevatorInput elevatorInput = new ElevatorInput(System.in);
while (true) {
……//获得请求,personNum++;
}
for (int i = 0; i < personNum; i++) {
RequestCounter.getInstance().acquire();
}
this.mainRequestTable.setEnd(true);
elevatorInput.close();
}
Elevator
public void Out() {
ArrayList<Person> outPeople = this.paxMap.get(this.nowFloor);
for (Person one : outPeople) {
TimableOutput.println(String.format("OUT-%d-%d-%d", one.getID(), nowFloor, id));
RequestCounter.getInstance().release();//完成请求
}
this.nowNum -= outPeople.size();
this.paxMap.get(this.nowFloor).clear();
}
RequestCounter
public synchronized void release() {
cnt++;
notifyAll();
}
public synchronized void acquire() {
while (true) {
if (cnt > 0) {
cnt -= 1;
break;
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这个过程简单来说,就有点像对帐单一样,InputThread根据输入情况获得账单数personNum,当elevator完成一个请求,就递一个账单(cnt++;)并通知Inputthread进行销账(cnt--),当还没有账单递过来时,并且目前账单没有报完时Inputthread进入等待状态等待唤醒。全部解决后,Inputthread给总池子设置setEndFlag
RequestTable、Person、Elevator中都写了克隆的方法ShadowElevator类,这个类的作为Elevator的子类,需要重写Elevator中的方法不应该设计在reset前尽可能多跑两层,因为可能会出现这样的情况:
电梯将要输出一个ARRIVE时,输入线程得到该电梯的Reset请求,这样的话这个ARRIVE就不会被记入到cnt中,所以在reset前会跑出来3次
在我的设计中,不应该采取把请求重新扔回总池的分配的策略,因为这样,就意味我们无法对reset的电梯进行模拟(影子电梯一次只能模拟一步电梯的运行状况),所以可供我们模拟的影子电梯的数目会变少,有的电梯可能会承载更多的请求。

沿用了上次的架构,调度策略换成了顺序分配
maxFloor、minFloor、isDouble、section当电梯前往maxFloor、minFloor时,需要检查controller中的isOccupied是否被置位。
并且双电梯不能同时进入缓冲层。
引入controller类---管理上下电梯的冲突
运行策略调整:
上下电梯同时想要前往缓冲层时,会同时访问isOccupied,这就会造成线程安全问题
我们解决的手段就是加对象锁
synchronized (controller) {
if (controller.getStatus() == false) {
controller.setOccupied(true);
return Advice.MOVE;
} else {
controller.waitForTrans();
controller.setOccupied(true);
return Advice.MOVE;
}
}
初此以外,我们的电梯不应当在缓冲层进行wait或者over.
电梯线程在运行时,第一个执行的指令是判断Max是否等于min,如果相等说明是上层电梯,,调用controller.initWait();等待唤醒
public void run() {
try {
while (true) {
if (maxFloor == minFloor) {
controller.initWait();
}
……
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
当下层电梯重置好后就调用controller的方法,将另一部电梯唤醒
public void Reset() {
try {
……
if (thisOne instanceof DoubleCarResetRequest) {
resetTwo(thisOne, outPeople, needAlloc);
controller.notifyAnother();
}
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
双电梯线程要额外注意结束的问题,当上层电梯已经被唤醒那么它结束时就会正常的结束,那么未被唤醒的电梯呢?
所以下层电梯在执行Over前要经过controller通知上层电梯,同时我们要修改Strategy策略,让它执行的第一个判断就是
public Advice getAdvice() {
if (maxFloor == minFloor) {
return Advice.OVER;
}
……
}
系统分析
发现的问题一:
发现的问题二:
本该在36.1秒处执行的reset,直到程序的最后才执行
此前无任何有关elevator1的输出
推测:elevator1 陷入wait,直到被setend()唤醒



