第二单元总结
一、程序分析:
1、本单元第一次作业:


在本次作业中主要采用了生产者-消费者模式,RequstProducer为生产者,功能为读取需求,并put到平台上。Waitlist类为平台,有着一个储存Person需求的列表。Waitlist里面实现的主要功能有三个,一个put方法,两个take方法。Elevator电梯作为消费者,会调用Waitlist里的两个take方法,第一个take方法是当电梯里没有人时,在Waitlist里面取第一个人,作为电梯的主任务。第二个take方法是在电梯送人的时候,向Waitlist中取满足捎带规则的乘客需求。Control类的功能用于当生产者线程结束后,当wailist里的请求列表为空,电梯里没有人时,将电梯线程结束。
Waitlist和Control是生产者和消费者线程都要调用的,考虑到线程安全的问题,对共享变量上锁。对Waitlist里没有请求时,电梯采用轮询,这种方法会占用大量的CPU资源。
2、本单元第二次作业:


本次作业,在第一次的基础上,增加了多部电梯的需求,以及最大载客量等问题。
针对本次作业,我采用了一个生产者,多个平台,多个消费者的模式。首先我对第一次作业的结构进行了一些调整,将生产者并入了Main中,将结束电梯线程的工作交给了Waitlist。对于多电梯的问题,在Main里创建了一个储存电梯的列表,为每个电梯建立一个Waitlist,Main采用平均分配的策略向每个电梯的Waitlist里增加请求,电梯线程再到Waitlist里取需求。
被生产者和消费者共同调用的平台就是每个电梯的Waitlit,对于其中的共享数据进行加锁。当Waitlist空了的时候,电梯取人时不再采用轮询的方式,而是让电梯线程进行wait,当生产者put进新的需求时,notifyall,或者当生产者线程结束时,调用Waitlist中的方法,进行唤醒,在这种情况下,使电梯线程结束。在本次作业中,不再使用轮询,节省了大量的CPU资源,类的个数较第一次作业有所减少,但是Main里面代码长度长了很多。
本单元第三次作业:


本次作业,提出了换乘的问题,以及动态加入电梯。
这次作业与第二次作业的最主要的区别是增加了一个Channel类,用于将生产者产生的需求,分配给各个电梯的Waitlist。在这个类中,对乘客的需求进行分析,判断是否要进行换乘,再分配给合适的电梯。分配策略为如果可以直达,则选择直达电梯类型中,Waitlist中乘客最少的电梯。如果必须要换乘,那就分配给能够接乘客的电梯类型中,速度最快,等待乘客最少的电梯,当进行换乘时,电梯更新乘客请求,调用Channel中的方法,对乘客进行再分配。
Channel和Waitlist都是不同的线程需要调用的类,对于涉及到共享变量的方法进行加锁。定义一个变量,表示未完成的乘客数,当生产者线程结束,并且乘客需求全部完成时,结束所有的电梯线程。
二、第三次作业架构分析:
SRP:Main没有很好的遵循这一点原则,Main除了进行上层整体的构架之外,还起到了生产者的作用。而其他的类,Channel负责分配需求,Person定义了需求的整体,Waitlist作为生产者和消费者的平台,Elevator完成任务都比较符合该原则。
OCP:在本次作业中,并没有使用继承,而是在类上面进行修改。
LSP:在本次作业中,未采用自定义继承。
ISP:在本次作业中,为采用接口。
DIP:Channel和Elevator通过Waitlist进行通信,降低了耦合度,提高了独立性。
三、bug分析:
在第二次作业中由于轮询带来的CPU资源占用过多带来的bug,通过只用wait来代替轮询得到了很好的解决。其他的bug基本上都出现在线程如何正确结束的问题上,在第三次作业时,出现当换乘的乘客从第一次的电梯下来的时候,第二次应该乘坐的电梯线程已经结束的bug,解决方法是,设置一个变量,记录未完成任务的数量,只有当生产者结束,并且所有任务都完成了,电梯线程才会结束。
在互测中,没有成功找到别人的bug。
四、心得体会:
在线程安全方面,要关注好共享变量,将共享变量集中起来进行处理,对于不同的共享情况,可以采用不同的加锁策略,来实现更高的效率。
在设计原则方面,应该更好的遵循SOLID原则,在动手之前,多进行思考,设计一个扩展性强的架构,对于后面的迭代至关重要。

浙公网安备 33010602011771号