BUAA_OO_Unit2总结

本单元作业主要考察多线程之间的协作,主要的难点在于多线程的控制,需要保证临界资源的安全性。

一、第一次作业

1、类图

 

2、调度器设计

调度器基本采用了als策略,但主请求的选择稍有不同:在电梯没人时,将等候队列的第一个乘客作为主请求,电梯向着主请求方向运行。

调度器线程通过共享对象Warehouse和读入线程交互,通过共享对象和电梯线程交互。

本次作业每一楼座有且仅有一个电梯,每个电梯对应一个Warehouse,调度器储存读入线程分配的乘客,并在电梯到达乘客出发楼层时,将乘客分配给对应的电梯。 

3、协作图

 

4、bug分析

本次作业出现了输出线程不安全的问题。官方包中的

TimableOutput.println(s)

线程不安全,不使用同步块控制会导致输出的时间戳非递增。

二、第二次作业

1、类图

 

2、调度器设计

此次作业中每一层、每一座各有一个totalWaitQueue,每一辆电梯各有一个selfWaitQueue。增加了Dispatcher类,其功能是将totalWaiQueue中等候的乘客,分配到该层或者该座电梯的selfWaitQueue中。Dispatcher的分配方式为随机分配,电梯依旧采用hw5中的策略。

3、协作图

 

4、bug分析

本次作业中发现了上次作业遗留的bug:线程不安全。经检查发现,是因为WaitQueue中的方法没有用synchronized方法块锁起来,但是在Elevator中上锁的代码块中引用了WaitQueue中的某些方法,导致上锁失效,线程不安全。

三、第三次作业

1、类图

 

2、调度器设计

换乘的处理:首先每个电梯有一个自己的侯乘队列,各层、各座有一个总的侯乘队列,由调度器将总的侯乘队列中的乘客分配给电梯的侯乘队列。新建MyRequest类管理乘客,在该类中乘客增加属性flag,标志该层电梯是否能搭载该乘客。input中获得乘客后直接判断乘客最佳搭乘步骤,并按搭乘步骤设置相应的MyRequest对象,其中搭乘步骤中的第一步对应的MyRequest对象的flag位置1,若有其他步骤则将其对应的MyRequest对象的flag位置0,并加入对应的总的侯乘表。只有flag=1,且电梯能同时到达乘客的起点座和终点座时,调度器才能将乘客从总的侯乘队列分配到对应电梯的侯乘队列。乘客到达后,即完成搭乘步骤中的一步后,判断该层/座totalwaitqueue是否有该乘客乘坐步骤的下一步,若有则将该乘客flag位置1,表示乘客已到达该位置。

调度器设计:调度器的设计延续了hw6中的方式。

3、协作图

4、bug分析

本次作业的bug出现在换乘过程中路线的判断。主要原因是RequestProducer中表示每一层中每一辆电梯的抵达信息的属性:HashMap<Integer,ArrayList<Integer>> eleSwitchInfos,没有正确初始化。

错误代码:

public void initialSwitchInfos() {
for (int i = 0; i < 10; ++i) {
this.switchInfos.add(0);
this.eleSwitchInfos.put(i,switchInfos);

}
switchInfos.set(0,31);

eleSwitchInfos.get(0).set(0,31);
}

正确代码:

public void initialSwitchInfos() {
for (int i = 0; i < 10; ++i) {
this.switchInfos.add(0);
ArrayList<Integer> ele = new ArrayList<>();
ele.add(0);
this.eleSwitchInfos.put(i,ele);
}
switchInfos.set(0,31);
eleSwitchInfos.get(0).set(0,31);
}

错误代码中,eleSwitchInfos中储存的始终是整层电梯的可到达座,并没有实现储存“每一层中每一辆电梯的抵达信息”的要求,从而导致乘坐路线判断有误,存在乘客始终无法到达终点。

四、同步块的设置和锁的选择

三次作业中全部使用synchronized同步块,将线程中需要使用到临界资源的代码块全部使用synchronized标记并将临界资源WaitQueue中的方法全部用synchronized标记,保证同一时刻至多只有一个线程能拿到共享数据的“锁”,并对其进行操作。

五、Hack策略

这单元对hack的热情急剧下降。主要是因为多线程问题的bug比较难复现,且电梯单元的bug难以通过随机数据查找。

六、心得体会

1、层次化设计

多花时间进行架构的设计,减少类与类之间的耦合,方便后续作业的扩展,也更容易发现并修改bug。

2、线程安全

  • 理解线程wait()的条件
  • 临界资源中的代码都使用synchronized标记,避免因引用未标记的代码块,导致“锁失效”

3、一些其他感想

为了更好地完成本单元的线程作业,我在hw5作业布置前的一周里预习了多线程的相关知识,但在实际运用中还是不太熟练,导致hw5中出现了线程不同步的问题,且延续到了hw6。此外,在hw7作业中,我没有做好课下测试环节,hw7强测和互测中出现的bug我本可以自己在课下针对性地构造一组数据进行测试,但因为比较顺利的通过了中测和弱测,我便自以为没有问题,也就没有进行额外的测试,导致强测和互测中出现较多bug。

这次的电梯作业算是安全过关,但下一单元需要谨记hw7的教训,自己能预料到的可能出现的错误,一定要测试一遍!

posted @ 2022-05-03 14:55  Hu_ly  阅读(17)  评论(1编辑  收藏  举报