OO第二单元总结—多线程电梯调度

OO第二单元总结—多线程电梯调度

前言

OO第二单元的作业,开始学习多线程,自己构造的电梯开始在多线程下开始送客。第一次作业只有一个傻瓜电梯,也不用考虑优化,只需要从输入队列中取出一个请求,把他送达目的地,再取下一个请求。因为对CPU运行时间没有特殊要求,因此即使进行暴力轮询,不断查看是否有新请求也是可以的。总的来说第一次是熟悉多线程的最基本运作。第二次作业就有所不同,加入优化后交互性就变得更强,当有新请求到达后,你需要很快更换策略做出决定来调度电梯。而最后一次作业三个电梯同时运行,电梯之间,电梯与调度器之间的交互都要注意线程安全,对线程安全有了更高的要求。

 

第一次作业

1.1 设计策略


第一次作业简单的采用消费者生产者模型,使用电梯和输入两个线程,请求队列为仓库,在add()get()中对RequestQueue加锁。输入线程将请求放入队列中,电梯每次完成一个请求后从请求队列中取出一个请求。队列为空,请求线程没有结束时,则进入轮询状态,有新请求入队则立即取出执行。队列为空,请求线程结束,则电梯线程结束。

1.2 复杂度分析

类图

度量分析

Elevator中的run()方法iv(G),v(G)比较高,因为电梯的运行,与请求队列的交互都在这个方法中,同样电梯线程的WMC和OCavg也比较高。因为电梯由于是傻瓜电梯,所以电梯的运行判断都集中在了该类中导致该类的循环复杂度过高。电梯的运行控制应该单独拿出来作为一个control类,各司其职,而不应该挤在一个类中,这是一个要改掉的点。

 

1.3 UML协作图

 

 

第二次作业

  2.1 设计策略

第二次作业其实与第一次作业的设计基本相同,但是与第一次作业的每结束一个请求不同的是,第二次作业的电梯需要即使不断的与调度器进行交互,从而更新策略,进行人员的捎带。

由于无法进行暴力轮询,因此,电梯在无请求时进入wait状态,有新请求入队时将其唤醒。同时,为了防止输入线程直接结束,输入线程在结束时也负责将电梯线程唤醒。

2.2 复杂度分析

类图

度量分析

第二次的作业中,同样elevator.run()与其他方法的紧密程度很高,也是因为与调度器进行交互引起的。其次,controller.checkNext()的方法结构复杂度和循环复杂度过高,这是因为在这个方法中,调度器需要对电梯运行方向的其余楼层进行遍历,判断电梯是否需要转向,或许可以直接使用类似差分的方法记录某层上或下是否有请求,从而避免遍历判断。

 

2.3 UML协作图(与第五次类似)

 

 

第三次作业

  3.1 设计策略

第三次作业由于加入了换乘操作,因此如果只有一个调度器会比较复杂,因此我使用了一个主调度器和三个子调度器,主调度器接受请求并分发任务给三个子调度器。子调度器进行电梯的调度。当有换乘请求出现时,主调度器决定换乘楼层并将任务下发给子调度器,子调度器在完成换乘的第一部分后,将第二部分的换乘作为一个新的请求发给主调度器,再由主调度器进行任务的下发。

由于有多部电梯,因此电梯的结束条件需要同时对所有电梯的请求队列以及输入线程进行判断。同样,电梯在wait()的过程中,输入线程结束时以及其他电梯线程结束时,都需要将等待的电梯线程唤醒,防止电梯无法唤醒。

3.2 复杂度分析


度量分析

 

第七次作业的九个类中,主要进行运作的主调度器,子调度器和电梯线程的总循环复杂度都非常高。主要原因很显然是因为类的方法过多以及类在进行调度和运行过程中使用了很多if() while()等进行判断。或许对于调度器可以再进一步进行拆分,将需要transfer的请求单独进行分类。以及同样,电梯只负责运行,可以将电梯的运行的控制与调度器之间再增加一个类,控制电梯的运行。

 

3.3 UML协作图


 

 

SOILD原则分析

 其实在完成本单元作业时,我对SOILD原则是,完全没想起来,同样也没有去遵循。但是我还是对我的作业进行一次SOILD原则分析,一方面是对这个原则进行学习和加深印象。另一方面,也要求自己在下一次的作业中,通过SOILD原则对自己的作业进行规范和分析。

由于我的本次作业基本没有设计继承与接口,因此LSP-里式替换原则,ISP-接口隔离原则以及DIP-依赖反转原则不进行分析。对于SRP-单一功能原则,这个原则我感觉理解上十分简单,做起来却十分困难。对于这三次作业的电梯类和调度器类,影响电梯类的是电梯的运行方式,影响调度器类的是调度器的调度方式。而对于OCP-开闭原则,从第一次到第三次作业,调度器只是增加相应的调度策略,如换乘人调度等。但是电梯运行没有很好的遵从这一原则,三次电梯线程都有了很大的变换,run()方法有了很大的变化。

 

BUG分析

  第三次作业的bug

CPU_TIME_LIMIT_EXCEED

测试样例

 [0.6]581865681-FROM-4-TO-17
 [37.0]1826570739-FROM-15-TO--3
 [37.0]1898692585-FROM-12-TO-15
 [37.0]1071007285-FROM-2-TO--1
 [37.2]1769633790-FROM-7-TO-19
 [37.4]1177817012-FROM-8-TO-11
 [37.4]700992729-FROM-10-TO-11
 [37.5]170602116-FROM-6-TO-13
 [37.6]1117581516-FROM-18-TO-5
 [37.6]1076433176-FROM-1-TO-12
 [37.6]806898994-FROM-3-TO-4
 [37.7]1108638514-FROM-5-TO-3
 [37.8]2077196564-FROM--3-TO-5
 [37.9]450897991-FROM-17-TO--2
 [37.9]1346062479-FROM-15-TO-19
 [37.9]218829325-FROM-10-TO-1
 [38.2]579934722-FROM-8-TO-1
 [38.3]1253720331-FROM-2-TO-12
 [38.3]1558891210-FROM--2-TO--3
 [38.3]408601421-FROM--2-TO-14
 [38.4]276981393-FROM-10-TO-13
 [38.5]1737896177-FROM-17-TO--3
 [38.6]1254506528-FROM--1-TO-18
 [38.6]1847860662-FROM--2-TO-4
 [38.8]274450133-FROM--2-TO-7
 [38.9]1655909809-FROM-5-TO-3
 [39.0]1586461491-FROM-18-TO-6
 [39.0]131299067-FROM-20-TO-19
 [39.1]1362960999-FROM-11-TO-5
 [39.1]1301835977-FROM-9-TO-16
 [39.1]338045772-FROM-12-TO-10
 [39.1]133744241-FROM-4-TO-7
 [39.2]768510074-FROM-2-TO-19
 [39.3]1078583130-FROM-4-TO-20
 [39.3]898341086-FROM-18-TO-3
 [39.4]1639141227-FROM-10-TO-17
 [39.4]1559388293-FROM-19-TO-17
 [39.5]683904324-FROM-10-TO-15
 [39.6]76208388-FROM-3-TO-5
 [39.8]1332052832-FROM-10-TO-14

报错

 xception in thread "Thread-1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
  at java.util.ArrayList.rangeCheck(ArrayList.java:653)
  at java.util.ArrayList.get(ArrayList.java:429)
  at homeworkseven.SubController.enterChange(SubController.java:145)
  at homeworkseven.Elevator.close(Elevator.java:119)
  at homeworkseven.Elevator.run(Elevator.java:167)

错误原因

错误原因并不是因为线程不安全,而是因为removeAll()方法使用错误。在主线程进行换成任务分配时,我使用了ArratList储存换乘人的换乘楼层,与请求一一对应。由于电梯有人数上限,因此我在电梯进人时选择一个一个进入,并且把进入的人进行储存,在进入结束后removeAll()统一进行删除。但没有想到removeAll()并不是一一对应删除,而是将交集全部清除,造成换成楼层无法与请求对应,造成了越界。

 

心得体会

 从这三次作业中:
  • 最基本的,我学习并了解了多线程的知识,基本具备了一定的保护线程安全的观念。与之前的作业只关注需求不同,电梯作业更符合真实的场景,关注需求的同时还应注意线程的交互安全。

  • 其次,我对CPU实际运行时间有了一个更深的认识,当while()进行不断请求询问时,CPU时间很容易就超出上限。

  • 多线程的调试和debug不同与单线程,多线程出现的bug具有复现性,相比与单线程, 多线程的调试,debug的过程更加困难。

  其次我认为我有必要好好学习一下互测的方法,虽然我简单写了一个将下载的压缩包到直接读文件输入,将输出写进文件的脚本,但是我并没有进行数据的自动生成,定时投放,以及正确性判断等高级操作。导致最终互测的结果并不理想,甚至自己在第七次作业中还出现了bug,直接凉凉的情况。

  最后感谢老师和助教的付出和教导。

猜你喜欢

转载自www.cnblogs.com/tsfics/p/10746333.html