BUAA_OO_第二单元总结

面向对象第二单元总结

一、同步块的设计和锁的选择

​ 在这三次作业中我设计了三个线程,一个是输入线程InputThread,另一个是Scheduler,最后一个是Elevator。 在这一次作业中我采用的模式是生产者和消费者模式。InputThread相当于生产者不断地产生乘客。Elevator相当于消费者不断地去处理乘客。其中我设置了PassengerPassengerList以及ProcessingQueue三个类来达到获取乘客以及待处理乘客的效果。

​ 其中在InputThread中我对于PassengerList以及ProcessingQueue进行了上锁、Scheduler中对于PassengerList以及ProcessingQueue进行了上锁以及Elevator中对于processingQueue进行了上锁。我采用的上锁的方式均为synchronized。在这里我对于锁的思路大致就是程序执行的思路。当InputThread线程传入东西时,我需要对PassengerList上锁并往里传入乘客。其他的地方也同理。锁与同步块中处理语句的关系应该就是当多个线程需要对共享的对象进行写入和读入的操作的时候,需要对其上锁处理。

​ 在这里为了减少锁之间的耦合,一个对象我不会跨线程调用,防止出现执行顺序不同,导致死锁的情况。例如PassengerList我只在InputThread以及Scheduler线程中使用并不会到Elevator中,降低耦合性。

二、调度器设计

​ 第五次作业虽然只有一部电梯,但是为了之后迭代的方便。我三次作业均添加了调度器。调度器顾名思义,要通过输入将各个电梯调度起来。在这里我通过对PassengerList的读取以及每个电梯的ProcessingQueue的写入,来达到调度的效果。

​ fen()就是将passenger分给每一个电梯的侯乘列表。

1、电梯的分配策略

​ 当乘客来的时候,我先后一共采用了三个方法,第五次作业只有一个电梯,不需要调度。之后的电梯我考虑的是产生一个随机数,让电梯随机调度,还有一个是平均。这两个是我为了防止我没时间所采取的方法。最后一个就是我分析每个电梯的楼层以及运行方向再结合该乘客的起始楼层以及终止楼层,来相应地去选择哪部电梯乘坐。

​ 在进行了相关的测试数据之后,我选择了最后一种调度的方法。

2、电梯内部的调度

​ 我采用了面向对象的思想,将每一个板块分离了出来。Elevator中只对自身的ProcessingQueue进行读取。只要有乘客我就采取look算法进行运载。

​ 电梯内部我对于Morning这个策略有些犹豫,不知道是该等还是不该等。最后我发现如果Morning策略下如果我装了一个人就走了的话。在运行期间,一楼会源源不断的来人。所以当我电梯第二次下去的时候,又能够把这些人全部装入。所以性能上并不会差太多。不想写三个策略所以我就将这些策略融合成了一个。通通采用look进行运载。

三、功能设计与性能设计平衡分析以及第三次作业架构的可扩展性

第五次作业的UML图和协作图

第六次作业的UML图和协作图

第七次作业的UML图和协作图

1、功能设计与性能设计的平衡方面

​ 前面也提到了,我的设计为了降低耦合度,每个类只做自己的事情,分工明确。为了求稳,我没有做更多的优化。因为上一个单元因为优化崩了三次

​ 功能设计我参考的是现实生活中的电梯的实现方式,采用look算法来实现电梯的运行。电梯根据第一个乘客的请求确定方向,然后根据之后的请求判断是否往上或下。或者是跳转方向。在第七次作业换乘的问题上,因为我一开始想的方法,会比较慢,所以我采用了低楼层到高楼层优先采用C电梯的方法,就没有采取换乘。分数还可以

​ 在平衡方面,为了让程序不出bug,稳了一手。只是简单地对于Scheduler进行了调度方面的优化。

2、第三次作业架构设计的可扩展性

​ 通过UML图可以看到程序结构十分清晰,耦合度较低。InputThread负责输入的处理。Scheduler负责调度乘客给电梯。Elevator根据自己的属性(A、B、C)来处理ProcessingQueue中的请求。

​ 在我看来结构简单,耦合低应该扩展性可以很高。各个线程之间分工明确,不容易出现执行顺序造成的意想不到的bug的情况。

四、自己的bug

​ 第五次作业是因为Morning策略的时候,发生了死锁。因为我参考的是上机实验的代码。在实现上还是存在差别,导致了死锁。

​ 第六次作业是ElevatorScheduler中锁的位置以及同步块的范围加错了,本不应该在synchronized中的代码段也被包括进去了,导致有线程wait的情况,造成超时。

这个地方一开始我是用一个synchronized。但是这样会导致我的processingQueue不更新,导致程序错误。当采取两个锁的时候。processingQueue就有机会被Scheduler中获取进行更新。

五、互测策略

​ 在这个单元,因为没评测机,很难去发现bug。主要的还是静态看代码。分析锁以及代码同步块的逻辑,以及整个线程的执行顺序。

​ 正如纪一鹏老师所说,当你看一个代码短时间看不懂,觉得逻辑乱七八糟的时候。十有八九有bug。当我发现有同学锁上的十分随意或是逻辑很混乱的时候,我就会有意构造一些数据去测试。

很遗憾没测到 可能是多线程的bug真的很难够去复现。

六、心得体会

​ 在这个单元我对于java这个面向对象有了更深的认识。第一单元的第二次作业作业我居然用一个类写完了 懂得了类如何科学的去设计和使用。懂了去有意识地降低程序的耦合度,使结构清晰。并且对于线程安全也有了自己的认识,当需要用到的对象,同时又被别的线程使用,同时会被读入和写入的时候,就需要加锁。在执行完后,将锁放出。当使用wait的时候,脑中一定要清楚,什么时候把它唤醒,程序快要结束的时候,各个线程是如何运行的,是否都结束了。防止出现“一张票被多次卖和卖不停的情况orz”。

​ 层次化设计的思想,应该说相比于第一单元提升了不少。懂得了将复杂问题抽象化,懂得去把共性的需求融合起来,用一个类去解决,每个类只管自己的“事情”。像这个单元的调度器就是一个层次化的体现。将生产者消费者的模式层次化实现,结构十分清晰。

​ 还有一个就是我对于时间安排上有了一个比较大的进步,第一次作业几乎每次都是周天交,这一次几乎周六早上就写完了。可能是熟练度的原因我觉得主要原因还是迭代的好处。这次作业基本没有大的改动,通过将代码层次化设计后,扩展性得到了巨大的提升,每次作业也就加了100行左右的代码。再有一个原因可能是信心的提升,没有那么的畏惧每一次的作业,而是十分愿意去想,去解决问题。希望在之后的OO作业和以后的学习生活中也有这种不断向上的精神态度。

posted @ 2021-04-25 11:01  Tao-30  阅读(56)  评论(1)    收藏  举报