前言
第一次作业
1.1 设计策略
第一次作业简单的采用消费者生产者模型,使用电梯和输入两个线程,请求队列为仓库,在add()
和get()
中对RequestQueue
加锁。输入线程将请求放入队列中,电梯每次完成一个请求后从请求队列中取出一个请求。队列为空,请求线程没有结束时,则进入轮询状态,有新请求入队则立即取出执行。队列为空,请求线程结束,则电梯线程结束。
1.2 复杂度分析
类图
度量分析
1.3 UML协作图
第二次作业
第二次作业其实与第一次作业的设计基本相同,但是与第一次作业的每结束一个请求不同的是,第二次作业的电梯需要即使不断的与调度器进行交互,从而更新策略,进行人员的捎带。
由于无法进行暴力轮询,因此,电梯在无请求时进入wait状态,有新请求入队时将其唤醒。同时,为了防止输入线程直接结束,输入线程在结束时也负责将电梯线程唤醒。
2.2 复杂度分析
类图
度量分析
2.3 UML协作图(与第五次类似)
第三次作业
第三次作业由于加入了换乘操作,因此如果只有一个调度器会比较复杂,因此我使用了一个主调度器和三个子调度器,主调度器接受请求并分发任务给三个子调度器。子调度器进行电梯的调度。当有换乘请求出现时,主调度器决定换乘楼层并将任务下发给子调度器,子调度器在完成换乘的第一部分后,将第二部分的换乘作为一个新的请求发给主调度器,再由主调度器进行任务的下发。
由于有多部电梯,因此电梯的结束条件需要同时对所有电梯的请求队列以及输入线程进行判断。同样,电梯在wait()
的过程中,输入线程结束时以及其他电梯线程结束时,都需要将等待的电梯线程唤醒,防止电梯无法唤醒。
3.2 复杂度分析
度量分析
3.3 UML协作图
SOILD原则分析
由于我的本次作业基本没有设计继承与接口,因此LSP-里式替换原则,ISP-接口隔离原则以及DIP-依赖反转原则不进行分析。对于SRP-单一功能原则,这个原则我感觉理解上十分简单,做起来却十分困难。对于这三次作业的电梯类和调度器类,影响电梯类的是电梯的运行方式,影响调度器类的是调度器的调度方式。而对于OCP-开闭原则,从第一次到第三次作业,调度器只是增加相应的调度策略,如换乘人调度等。但是电梯运行没有很好的遵从这一原则,三次电梯线程都有了很大的变换,run()
方法有了很大的变化。
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时间很容易就超出上限。 -