第二单元总结

第二单元作业

这一次作业的主要内容是模拟电梯。第一次作业是简单的五部电梯,并且需要实现捎带;第二次作业则是加入了横向电梯,同时增加了加电梯的指令;第三次作业需要实现换乘,并且横向电梯加了开门楼座限制。

同步块设置和锁的选择

同步块设置

  由于第一次作业中对多线程方面的知识还不太了解,因此直接参照课上实验代码来写,在RequestQueue中的所有方法前加了synchronize,在之后两次作业的同步块设置上也没有做更改。

锁的选择

  在这三次的作业中我全部采用了synchronize的方法来上锁。虽然在课上又讲了新的ReetrantLock锁,使用起来更加灵活,但是syschronize实现起来比较简单,因此我没有更改上锁方式。

架构

第五次作业

基本思路

  本次作业是我第一次接触多线程编程,因此我在开始做之前花了很多时间去理解Java多线程的使用,包括synchronize、notifyAll。好在有课上上机的代码能够借鉴,于是我以此为模板完成了第一次作业。

  在本次作业中我采用了look算法,也就是从一楼开始,先取一个向上且始发楼层离1楼最近的请求,沿途一路接上同方向的请求(不超过人数上限);当不存在同向且出发楼层更高的请求时,取反向且出发楼层最高的请求,使电梯达到那个楼层并反向运行。如此往复,直到处理完所有的请求。

  这次我用了六个类。Elevator类是电梯线程,用来模拟电梯状态和行为,其中包含了电梯ID、运动方向、开关门信息、当前楼层、所在楼座、当前乘客、之后预备上电梯的乘客等信息;Input类是输入线程,用来读取指令,并且由于这次作业要求为五个电梯、每个楼座一部电梯,所以不需要调度器便可完成,于是我将分配乘客的操作也放入了Input类;ReqeustQueue类是每个楼座的等候队列;Output是封装的输出类(为了保证时间戳递增);Passenger类是电梯的乘客类,在这次作业中其实可以直接使用PersonRequest,但考虑到之后换乘的情况,为了方便扩展就新增了一个类;最后还有一个主类Main。

 

代码分析

UML图

  

bug分析

  在此次作业中,我没有实现好我预想的策略,导致很多时候会出现电梯里只有一个人的情况,很多强测点因此过不了,于是我在之后修改了策略的实现方式。

第六次作业

基本思路

  本次作业加入了横向电梯和新加电梯请求。横向电梯总体上来说和纵向电梯并无差别,主要是可以循环。在实现横向电梯时,我本来想用继承基本电梯类的操作来完成两种电梯,但发现这样会调用很多次getter和setter,因此我只是额外新增了一个横向电梯类和一个横向电梯请求队列。

  对于横向电梯的策略,我直接采用了单向旋转的策略:电梯只会按ABCDEA的顺序行进,遇到人如果不超载就接上,如果没有人就取最近的请求。虽然在某些特定的情况下会很慢,但是总体来说效率不会特别差;并且这么写代码比较简单。

  对于新加电梯的请求,我也是在Input类里直接读取完就start。同一楼层/楼座的电梯共享同一个横向/纵向队列,采取自由竞争的策略,没有使用调度器进行调度。

代码分析

UML图

 

 

 

 

  

bug分析

   这次作业在强测中没有被发现bug,但是在互测中被一个数据hack了。这个数据是在几秒钟的时候加入两部电梯,之后隔了比较长的时间再进人。由于我在等待队列中的isEnd方法中加入了notifyAll,而电梯等待的判断条件中需要调用这个方法,于是线程被频繁唤醒睡眠,导致CPU时间超过限制。

 

第七次作业

基本思路

  本次作业加入了换乘,并且横向电梯只能在给定楼座开门。由于有换乘需求,所以需要有一个调度器。我新增了一个Controller类,使用了单例模式,把原先Input中的一些功能加入其中,主要负责start新的电梯线程和向电梯等候队列中添加乘客。

  为了防止没有输入之后还有需要换乘的乘客没有到达,我添加了一个Counter,确保每个请求最终到达了目的地再使电梯停止运行,同样也使用了单例模式。

  对于横向电梯的可达性,我采用了实验中提到的BitSet的方式,相当于一个boolean数组,使用起来比较方便。

代码分析

UML图

 

 

 

bug分析

  本次作业出了比较多的bug,一个是电梯进人时未注意到达楼座能否开门。

  另一个是在确定中转楼层时出现了bug。我在存储每个楼层中楼座的可达性时采用的是BitSet的ArrayList,新加入电梯就进行or运算。但是这样就导致了一个问题:如果一个楼层中一个电梯是ACE,另一个电梯是BD,他们的BitSet进行or运算之后是31,但是这个楼层实际是无法进行换乘的。后来我将其改成了ArrayList<ArrayList<BitSet>>的形式,例如switchInfo.get(0).get(i).get(j)表示的是第一层第i-1+'A'座能否到达第j-1+'A'座。

Hack出的bug

  这几次作业hack的难度比上一单元大很多,因此房间内hack数下降了。我hack出的bug大部分是各种各样的原因导致超时,还有一些则是会产生Wrong Answer。主要是通过提交随机数据发现的。

心得体会

多线程的bug

  这个单元中我学习到了多线程的使用。这对我来说是新知识,和原来的编程有些不一样,需要考虑线程安全(包括官方包输出不安全的问题,很多同学都在这方面出现了bug),还有死锁、轮询、wait中不断被唤醒等问题。同时多线程的debug在一开始也造成了比较大的困难,因为没有办法使用调试,只能通过在程序中添加输出的方式来查看。

单例模式

  我还学到了单例模式,它可以避免传参,让代码更加简洁。

关于电梯策略的体会

  荣老师在上课讲了电梯问题不存在最优解,每种策略在特定的情况下都有可能运行得比较慢,因此,主要需要考虑的是如何架构和如何确保策略的正确实现,不能忽视了这些而去过度追求局部的优化。

posted @ 2022-05-03 22:36  jht0725  阅读(34)  评论(0编辑  收藏  举报