面向对象第二单元总结
单元内容
本单元的内容是电梯的模拟运行,三次作业不断完善电梯的数量和功能,具体如下:
- 单部多线程可捎带电梯的模拟
- 多部多线程可捎带调度电梯的模拟(电梯数量是静态的)
- 多部多线程可捎带调度电梯的模拟(电梯数量是动态增加的)
多线程设计策略
第一次作业

因为只有一个电梯,不需要进行请求的分配,所以除主线程之外只设计了两个线程:请求输入和电梯
没有请求时输入线程可以自行结束,并通知电梯线程,电梯在处理完所有请求后结束
第二次作业

在第一次作业基础上,新增DispatchThread线程,负责将请求分配到各电梯中
请求输入线程可自行结束,并逐级通知 调度线程 和 电梯线程
第三次作业

线程的种类和第二次作业一致,改变了部分线程的产生和消亡方式:
-
电梯数量是动态增加的,所以
ElevatorThread线程由RequestInput线程产生 -
由于存在换乘,
DispatchThread线程和ElevatorThread线程相互制约,不能自行结束我的设计是:在调度器中判断是否已经处理完所有请求,若满足条件,直接
System.exit(0)强制退出
架构分析
第一次作业
采用worker-thread模式:RequestInput线程不断地将请求放入调度器Dispatch中,调度器将请求分配给各个Elevator,ElevatorThread线程运行电梯
由于只有一个电梯,所谓的 “分配请求” 只是把请求直接扔给电梯
第二次作业
架构没有太大变化,只是因为电梯数量不止一个,新开了一个DispatchThread线程专门进行请求分配
为方便结束条件的判断,我的分配方法是一次性分配当前
Dispatch中的所有请求,这对Real time 和 CPU time都是有损失的,然而这个问题我在第三次作业中也没有解决(……)
第三次作业
架构上有两点变化:
-
电梯有不同的种类,所以将
Elevator类抽象为 抽象类,派生出三种电梯,分别实现各自的换乘方法 -
为了实现换乘功能,新增类
ElevatorBuffer,记录各电梯扔出的需要换乘的请求,和各电梯的状态(是否将所有请求都处理完毕)
新增缓冲区来储存需要换乘的请求,是为了阻止电梯直接访问调度器,如果两者互相访问,极有可能出现死锁
SOLID设计原则检查
| 设计原则 | 反思 |
|---|---|
| SRP-单一功能 | 做得不好的地方是Elevator类,将电梯调度算法也包括进去了,算法的扩展性和替换性很差 |
| OCP-开闭原则 | 自认为做得不错,几乎没有修改已有实现,只进行扩展 |
| LSP-里式替换 | 在电梯上使用了继承,但还做得不够完善,子类的代码相似度很高 |
| ISP-接口隔离 | 不涉及(并没有新建接口,其实电梯算法这个部分可以用接口实现) |
| DIP-依赖反转 | 唯一的抽象部分是电梯,抽象关系很明确,所以符合 |
bug分析
本单元未出现bug
关于互测,同屋同学的bug真的很少,考虑到互测成本
(其实是懒了),并没有修评测机,最终也没有hack到别人(热爱和平)
这单元的bug特点是WA比重不高,印象中三次作业最高的一次也只占到50%左右,A屋的比重还要更少,所以对电梯逻辑的正确性检验并不是重点。如果是做大略的检查,可以让电梯运行的结果以折线图的形式呈现(受sbc学委的启发),这样例如电梯闪现、越界的bug就是一目了然的,也可以看出性能上的缺陷。
除去WA,对RTLE,CTLE的检查,就只需要拿到相应的时间数据,这个是比较容易的。
很多超时问题,都是算法的设计问题,和算法本身的正确性是无关的,这个时候用评测机生成的随机数据反而效果不好,应该用极端数据进行测试,比如大量的20 - -3等等。可惜我也是最后才悟到的。
心得与体会
-
关于设计原则
OCP-开闭原则是一个指导性的原则,在功能不改变的前提下,无需修改已有实现,只需要进行功能的扩展。如果各类不能做到各司其职,而耦合在一起,在扩展时就会牵动筋骨,很难不做修改。在这个原则的push下,自然就会想到要构造各种抽象结构,来增强程序的可扩展性。
-
关于线程安全
要破坏死锁,就要破坏死锁形成的必要条件,其中很重要的一点是不让各线程的资源请求形成一个闭环。可以在局部形成生产者-消费者模式,两个线程对同一个共享对象进行访问,而不是互相访问,从而避免死锁。
-
关于性能
这一单元我的性能分总体较低且起伏很大,分析起来也是比较玄学,但还是有几点可以总结:
-
首先一点是要充分发挥多线程的优势,不要直接把锁加到一个方法上,最好是可以找到线程安全的最小原子操作,对性能提升很明显。
-
第二点就是不同人采用的不同的电梯调度策略,用LOOK和SSTF的比较多,我是利用的SSTF算法,前两次比较失败(猜想这里LOOK性能更高一些?),总之都是可以的。
-
此外就是第三次作业中加入了乘客等待时间这一评价量,为了避免SSTF的“饿死”现象,我记录了各请求投入的初始时间,在空电梯进行请求选择时用 “楼层差 / 等待时间” 作为优先级,性能分有所提升。
-

浙公网安备 33010602011771号