一、从多线程的协同和同步控制方面,
分析和总结自己三次作业的设计策略
这三次作业,我都采用了同一种多线程的协同和同步控制。这是出于程序的线程安全性考虑,所以我选择了一种很简单的多线程架构。
输入--调度器请求共享队列--调度器--电梯请求共享队列--电梯
1 public synchronized Person getRequest(boolean flag) { 2 while (requestQueue.peek() == null) { 3 try { 4 wait(); 5 } catch (InterruptedException e) { 6 e.printStackTrace(); 7 } 8 } 9 return requestQueue.poll(); 10 } 11 12 public synchronized void putRequest(Person r) { 13 requestQueue.offer(r); 14 notifyAll(); 15 } 16 17 public synchronized boolean isEmpty() { 18 return requestQueue.isEmpty(); 19 }
共享队列的维护采用了wait(),notifyAll()的方式,是最基本的消费者和生产者模式。
虽然在过程中也了解了很多多线程的编程方法,比如concurrent的包,但是最后三次作业仍然延续了这种作业,因为想把出错概率降到最低吧。但是这样的代价有两个
1. 我的多线程知识掌握得不好
2. 第三次作业优化被架构局限了
二、基于度量来分析自己的程序结构
(一)第五次作业
1.类图
2.时序图
3.度量分析
3.1类
- OCavg:平均方法复杂度。
- WMC:带权方法复杂度。
3.2方法
- ev(G):核心圈复杂度。
- iv(G):方法设计复杂度。
- v(G):圈复杂度。
4.设计原则
- Single Responsibility Principle:基本符合单一职责原则。
- Open Close Principle:电梯硬编码,扩展性不强。
5.总体分析
因为第一次电梯的要求是VVVIP服务,一次只接送一人,所以设计上没什么难度,架构也比较简单。唯一注意的是第一次采取了硬编码,导致程序的扩展性不好。
(二)第六次作业
1.类图
2.时序图
3.度量分析
3.1类
- OCavg:平均方法复杂度。
- WMC:带权方法复杂度。
3.2方法
- ev(G):核心圈复杂度。
- iv(G):方法设计复杂度。
- v(G):圈复杂度。
4.设计原则
- Single Responsibility Principle:电梯类设计冗杂。
- Open Close Principle:电梯可扩展性强。
5.总体分析
这一次作业是我三次作业中写的最喜欢的一次,电梯采取LOOK策略。当电梯从调度器接到请求时,自动把调度按有序的方式存到upSet和downSet里。每一次电梯到达一个楼层会根据当前运行方向选取在当前楼层以上的最近楼层作为新目标。在每一次电梯执行完同向所有指令或者刚开始的时候会选区当前最优的策略作为新的目标楼层。(有的同学用的贪心算法,我用的是从一个简单的对出发楼层的数学逻辑判断。我粗略估计我的判断70%是和贪心的结果相同,而且所需时间和复杂度非常小。不过这两种方法都是局部判断,所以不必太过计较)对于这次作业的数据,我有考虑过电梯到底回不回头接人,折中了一下,我选择了不回头接人,虽然强测有两个点因为这个分不高。
(三)第七次作业
1.类图
2.时序图
3.度量分析
3.1类
- OCavg:平均方法复杂度。
- WMC:带权方法复杂度。
3.2方法
- ev(G):核心圈复杂度。
- iv(G):方法设计复杂度。
- v(G):圈复杂度。
4.设计原则
- Single Responsibility Principle:电梯类设计冗杂。
- Open Close Principle:由于三个电梯各有特性所以扩展性不强。
5.总体分析
这一次作业的三个电梯继承了上次作业电梯,在电梯单线程的抉择策略上应该是比较优的,但是这次作业主要的优化重点是电梯的配合上。由于线程架构设计的局限和LOOK算法设计方式带来的局限,我的电梯很难共享所有电梯当前的运行状态进行统一判断,研讨课上分享的同学就做得很好,向他学习。
电梯优化的方向主要是:
①打表选出每个请求的最优分配电梯,结合整体的需求曲线,适当平均分配。
②A电梯在1层和15层在电梯分配没有满的情况下可以返回处理请求。
③A电梯在满载的情况下可把合适的请求适当分给B,BC在满载的情况下可把合适的请求适当分给对方。
总的来说,虽然强测分还行,但是这次的电梯设计很失败。由于我的请求队列写死了,电梯状态不能互通,使得调度器和电梯内部只能特判而不能智能地分发,这两个类的代码十分冗杂。只有以后继续努力吧。
三、分析自己程序的bug和分析自己发现别人程序bug所采用的策略
因为在本次强测互测中,没有被发现bug,也没有发现别人的bug,所以我这个板块还是不太好写的。于是折中一下谈谈自己是怎么在提交作业前减少bug的。
我每次作业都会做一定程度上力所能及的优化,优化意味着大概率的增加很多bug。这种bug分为两类:1.考虑不周写错了;2.优化导致了原来不会出错的变化导致出错。
所以我debug的流程是:
1.黑盒随机数据测试
2.关键点覆盖性测试
3.针对优化点覆盖性测试
四、心得体会
(一)线程安全
我选择的是最简单的线程在一定程度上是比较安全的,在线程安全上遇到的问题有:
1.还没来得及读入就已经结束
2.读不到所有的数据点
3.如何优雅地杀掉线程
对于1. 我采取了延迟启动所有线程的方法,虽然不知道为什么这样做,但是成功解决了这一问题。
对于2. 主要第二次作业遇到的问题,解决策略是延迟读入电梯队列的时间,每读一条sleep(10)。虽然可以一次性读完30条数据,后来发现这对我的性能有很大的负担。
对于3. 我做不到太优雅,策略就是设置一个end线程,每一个线程持有一个end的共享对象,一个线程走到暂时的尽头会改变值,并且notify结束线程重新判断是否所有线程都结束了,如果是的话就结束程序。
(二)设计原则
1.单一职责原则:这一点我做得特别不好。主要是电梯线程的类,有一些功能和判断的方法全部保存在电梯类,导致代码十分冗长。
2.开闭原则:第二次作业把属性设置得灵活了,第三次作业还是比较好操作。但是一些个性的问题还需单独考虑。
五、总结的总结
这个单元学的不是很好,有点本末倒置了。因为担心出bug,而减少了多线程方面的尝试,这是挺大的遗憾。
继续加油吧。