面向对象课程第二次博客总结

第五次作业——多线程电梯

设计策略

实现三部电梯的综合调度,为每个对象线程明确任务分工。对象线程间通过发送睡眠中断信号实现事件通信。

Presser对象不断读取请求模拟按下按钮,生成请求并发送至请求队列中并向调度器发送新请求到达的中断信号。

电梯对象拥有一个任务执行队列,通过不断检查系统时间是否已到任务结束的假时间戳模拟执行这个任务队列完成请求,该任务队列由调度器负责添加新任务。电梯若执行完主请求,则暂停运行并向调度器发送中断信号请求分配任务。

调度器对象接收到中断信号后,尝试到请求队列中获取新请求,并获取三个电梯的状态分配请求,更新电梯的任务队列。

访问冲突存在于两个地方,一是调度器与Presser对象读写请求队列,而是调度器与电梯读写任务队列。那么就需要请求队列与电梯加锁。

度量分析

代码分析

类图

 

协作图

 

bug分析

次此作业没有什么大问题,互测中发现别人的程序在模拟电梯运行中直接使用真实时间模拟运行,导致因为线程调度存在阻塞,出现了累积误差,运行一段时间后,电梯的到达时刻会有0.1秒的正误差。在我的设计中使用的真时间模拟假时间戳,可以消弭这样线程运行调度引起的累积误差。

第六次作业——IFTTT

设计策略

监控文件系统的修改情况,一个监控对象的一种修改情况,对应设立一个触发器线程,触发器下可吊挂多个效应器。

每个触发器线程轮询文件系统,比对新旧快照,判断触发条件,若触发则启动该触发器下挂的效应器。

访问冲突在于触发器要读文件系统,效应器可能要写文件系统,那么为文件系统加锁,简单的synchronized悲观锁会互斥任何针对文件系统的操作,但其实我们希望的是,多个读者间是不互斥的,只有读写者、写者间保持互斥,这种加锁方式比较复杂,需要用到闸机模型,当时也不知道闸机模型,所以还是用的synchronized锁。

度量分析

代码分析

 

类图

 

协作图

 

bug分析

此次作业没有什么大问题

第七次作业——模拟出租车

设计策略

模拟100辆出租车载客运行。设计完全模拟现实生活中的场景,为每个对象做明确合理分工,各做各事。

InputerHandler对象负责读取请求并将请求发送至请求队列。

调度器对象负责从请求队列中获取新请求,并为每个新请求生成车辆抢单用的报名表,挂靠在地图上的相应的请求出发结点上,供过往的出租车抢单报名。抢单窗口关闭后,根据报名表上登记的抢单车辆信息和其目前的状态,为请求分配车辆。

出租车的任务是在城里闲逛,并查看附近地图结点下挂靠的抢单报名表,参与抢单填写本车信息,等待派单。如成功抢单,则执行送客任务。送客结束后重复上诉操作。

访问冲突在于调度器要在地图节点下发放和收取报名表,出租车要在地图节点下查看和填写报名报,因此为地图上的每一个结点加访问锁。

度量分析

 代码分析

 

类图

 

协作图 

 

bug分析

这一次程序在整体设计上没有什么大问题,只是出现了两个因为粗心导致的小bug。测试对面的程序时再一次出现了类似第5次作业的时间误差问题,被测程序直接使用真实时间模拟出租车的运行,因为线程调度的问题,有的车的运行时间就开始出现累积误差了。

心得体会

资源访问的安全问题是多线程设计的重要问题之一,在最初的设计过程中,就要做细致的分析和全面的考虑。首先要研究每一个对象的读写行为,寻找对象间的读写冲突点,并要根据不同到的读写特征选择适当的加锁方式,以避免过多的线程阻塞导致的效率下降,而不是只会简单的使用sybchroniezd锁。比如读者线程明显多于写者线程的情况下,就要考虑设计读者间的不互斥锁,否则的话,多个读者进程就会都被阻塞在临界区外,加重了调度负担,也消磨了多线程并发执行所带来的优势。

一个好的事前设计会给以后的工作无论是编码还是维护带来加速型的回报。但是事先设计也不可能考虑到所有的问题,很多问题是在编码过程中或是实际的测试中发现的。因此在编码完成填补bug时,不能只着眼于局部的问题处理,而是要结合整体设计判断是否找到了问题的根源,及时的做好新的注释,必要的话还需要重新整合设计,避免出现拆东墙补西墙,按下葫芦浮起瓢的情况,导致本来一个较好的整体设计,最终被改得面目全非,使得日后的代码维护变得十分困难。比如现在会看多线程电梯时,大体的设计还是看得明白的,但出现了很多细节的地方就看不明白了。这也提醒我们,设计并不是只存在编码之前,在编码时也有不断的动态设计,在编码结束后,也要回顾整体设计,针对新的问题对设计做更合理的抽象整合和重构。

posted @ 2018-05-02 14:35  何方程  阅读(128)  评论(0)    收藏  举报