301
社区成员
发帖
与我相关
我的任务
分享第二单元主要进行了多线程的学习,重点内容是解决线程安全问题。

第一次作业的架构相对简单基本为生产者消费者模型。
MainClass中生成了6个Elevater线程和一个InputHandler线程,同时为每个Elevater生成了一个RequestTable作为请求处理
Outputhandler为输出工具类,其中都是static方法,为输出提供公开方法。
每个Elevator在创建时会生成一个内置的调度器,调度器里面有RequestTable和Elevator的全部信息,采用Look算法,为电梯提供策略。
电梯收到相应的策略后,会依照策略执行相应的动作。
第一次作业相对简单使用synchronized 关键字实现同步互斥,简单来说,就是在RequestTable中的所有方法加入synchronized,确保Elevator在读写相应的数据时,InputHandler不会把新的数据写入。
采用look算法,look算法相比ALS算法更为优化,减少了无效运输的部分

第一次作业相对简单,没有出现bug,也没有hack到人。

第二次作业相对复杂了一些,在第一次作业的基础上:
第二次作业对于锁的设计思路与第一次作业一致,
在会产生读写冲突的地方ResetTable和所有的RequestTable中加入synchronized关键字
为了避免结束时死锁的发生,在结束时会对所有可能有锁的地方进行notifyAll,防止InputEnd后电梯陷入等待
第二次的调度器采用母子调度器,每个电梯上配置的小调度器采用look算法。
母调度器根据子电梯的性能、人数等打分,选择最优的电梯,把Person从AllRequestTable移动到该电梯的lRequestTable,进行请求分配。
电梯在Reset时会把所有的人重新放回AllRequestTable中(如果到了目的地,直接结束即可)

第三次的bug出现在没有为正在Reset的电梯设计buffer,即其优先级过低。如果其他5个电梯都在Reset,此时所有请求都会进入同一个电梯,造成TLE。
修复:增加buffer,如果分配到Reset的电梯,先放入buffer,等reset结束,从buffer中移动到相应的table并输出receive

第三次作业和第二次作业相比,有如下改动:
与第二次作业相同,Table类全部使用上锁方法。
把ResetTable从synchronized改成readwritelock,因为ResetTable读的频率远大于写,这样提高cpu性能。
简化设计,母调度器采用mod6的方法依次分配到相应的电梯井的RequestTable
电梯井收到后判断应该是A或B电梯运输,输出相应的Receive
对于双轿厢电梯 ,每个电梯的子调度器的顶楼设计从11层改为过渡层,如果目的地超出过渡层就把乘客放在过渡层,交给Allocator重新分配。
0本次设计为确保正确性和稳定性,一定程度上不是最优性能分。

第三次作业bug原因:结束时只看了所有PersonReset的finish,没有看双轿厢电梯的ResetFinish。
debug:增设双轿厢电梯ResetFinish信息的Hashmap,存放在Collection中。
在DoubleCarScheduler进行分析判断,作出如下设计:
第二单元学习了多线程安全,同时又深化了层次化设计的理解。
在线程安全方面,我们的bug主要出现在多并发的情况,结合老师上课讲的案例,我们在写多线程时应当时刻有多个线程的感觉,而不是写单一的程序。
同时,层次化设计为线程安全提供了保障,可以通过层次化设计,减少bug的出现,减少迭代过程中的复杂程度。
一味地追求性能分有可能不如确保程序的安全稳定运行更重要