第二单元的电梯真是愉♂快呢,多线程编程作为java编程OOP中的重要组成部分,通过这一个单元的学习,我也是有了很多全新的认识

那么下面就先例行一下公事


三次作业分析

第五次作业

设计分析

实现的电梯是很简单的,没有复杂的逻辑,主要目的应该也是帮助同学们入手多线程编程,加上课上对设计模式有所点拨,所以整体的设计应该是不难的,编码量也不大,只要处理好锁的关系和waitnotify的时机,不要出现死锁,基本是不会有什么问题的

下面给出我的设计,也就是中规中矩的生产者-消费者模式

这里我封装了自己的一个容器Requester(感觉什么东西加上个"者"后缀就很厉害呢),其实就是一个请求队列,实现了一下迭代器接口(后来发现不好用

然后就是电梯线程和电梯线程,调度器线程没有启用,这个设计主要是为以后的扩展做了一点准备,所以这里的Scheduler是个静态的

然后差不多就是这样了,邻接资源就是那个队列,使用的时候锁一下就好了

一个需要注意的地方是怎么控制输入结束的全局持续通知

这里是借用了信号量的思想,也就是设置了一个相应的属性,然后每次循环的时候查看一下这个信号,别的就没什么了

下面是整个程序的一些度量(忽略这只手)

因为有各种各样的输出语句,所以Elevator类的方法好像有点多了,不过我确定一定很短,很简单

你看

方法的复杂度都很低,看起来还是可以的

测试

对于多线程的程序我不是很清楚怎么做单元测试,单元测试也只能保证模块功能正常,但是我们都知道,真正的问题往往出现在线程的控制上的。

分享课上同学们分享的也都是各种黑盒测试(个人是比较鄙弃全部依靠黑盒测试的行为,盲目的随机胡测也许很有效,但是绝不优雅),所以也欢迎大家分享如何富有逻辑地进行测试。

第六次作业

第六次加上了捎带规则,加了点楼层,加了负层,0层神秘消失(误),整体来说笔者的设计是这样的。

三次设计的思路都是坚持做到单一职责,毕竟分割职责可以在做修改和扩展的时候放心大胆(我一向很大胆,其实是因为分隔职责后便于测试,良好的测试是修改的保险丝)。

所以就是

  • 输入线程只管输入,是一个无情的指令接收机器
  • 电梯线程只管执行,是一个无情的指令执行机器
  • 调度线程负责调度,完成(无情地)指令分发的任务

然后这样的设计就出现了

!

相比上次的设计,没太多变化,就是调度器动起来了,然后肩负起分配指令的任务

然后电梯中需要有一个队列维护自己持有的指令,线程变成了单步执行,每层看一看有没有上下开关门事件发生,逻辑不算复杂

调度器就是不断尝试将指令放入电梯中(后来笔者发现这里设计的不好,调度器空转太多了,加一个waitnotify机制会更好,或者是join,不过想来想去,抢占式或许是最优解,这个后面再说)

然后下面是整个程序的度量

可以看到调度器的复杂度比较高,其实主要是笔者作死的一个设计

笔者为指令和电梯运行加了一个方向的概念,因此自己给自己多设置了一层逻辑,没什么意义,也就是有MainRequestDirection两个量都表征运行方向,每次更新都要更新俩,判断的时候用哪个也说不清,确实是很糟糕的设计,笔者在后面也把这个量给废弃了

测试

关于这次的测试,笔者给出一些个人思考的可能出现问题的情况

  • 对于LOOK玩家,在电梯折返,或者说换向的时候打断,可能是一个边缘逻辑
  • 对于开关门和来指令的并发情况
  • 对于错过的指令的测试

第七次作业

设计分析

据说这是OOP作业难度的顶峰

鬼畜多电梯、负载、速度、随机突发随机指令,这几个情境下设计一个好的算法确实不容易

所以与其苦心分析设计,不如"实践检验真理",也就是通过本地测试时间来决定算法的性能(真是个小机灵鬼

所以在说我的设计之前,我想理性(瞎胡说)地分析一下这次调度算法的考虑

1. 关于接人和方向

(笔者很讨厌上楼的人在电梯下行的时候就上电梯),在本次作业中,出于载荷的考虑,也是加上同向捎带的判断,电梯的性能应该会好一些,但是实际检验发现,这个判断的效果并不明显,并且有的时候会出现负优化,个人分析原因是在于40条指令,三个电梯的总容量有20多,所以即使面对40指令并发,对于电梯们来说,工作量也不是很大。

正因如此,像我们考虑的情景——“很多反向请求的人上电梯,导致同向的请求上不去”,其实发生率也不高,所以在这种情景下,大吞吐反而是很优的。

2. 细节优化

对于这个评测方式,其实很多细节优化的效果也是不错的(只指分数,不保证绝对时间差),比如电梯的选择顺序,是快的优先还是随便来;是近的优先还是随便来。这种细节上的选择也会有不错的收益。

3. 抢占式很重要

如果让调度器主动分指令,电梯就要记着一会要接谁,即使这个人不在电梯里,出于安全考虑,电梯也必须为这个人留个位置,那么在这种情景下,电梯一般是一直装不满的。

本人的算法基本是硬调度,没有动态调度,调度规则都是硬编码的。(不像那些大佬们在电梯运行的时候动态通信)

程序的逻辑就是每次楼层改变和进出人后就去中间的Commander那里pull一下,看看有没有自己能拿走的,这样可以保证最大利用电梯容量

然后pull的逻辑是在Commander中实现的,Commander获取电梯的当前状态,然后判断队列中有没有他能拿走的

下面是程序的度量

看起来Commander好像不太高兴了,因为笔者不小心把指令分割的逻辑放到了Commander里面,这个是违反单一职责原则的,我在下面简单做一下拆分

可以看到,加上Spliter后,Commander的复杂度明显下降,此时Commander中只剩下和分配有关的逻辑

方法的复杂度还是很不错的

测试

这次测试笔者主要关注了下面这几个点:

  • 载荷,要保证不能超载,对于电梯载荷上限的数据要进行测试
  • 需要拆分的指令,对于需要拆分的指令,特别是可以多次拆分的指令,要重点测试
  • 效率,最好在测试的时候单步输出一下每时刻每座电梯中的人数,确保电梯有较高的载重

设计原则分析

  • SRP(单一责任原则):这次设计基本是符合单一原则的,唯一不足的地方是,电梯是主动去获取指令的,获取指令的逻辑也在电梯类里面,可以考虑专门做一个获取器,然后电梯每次调用这个类的对象
  • OCP(开放封闭原则):这三次作业其实需求扩展体现的并不明显,重点是需求变化,完全地不变更原设计是不现实的
  • LSP(里氏替换原则):这三次作业的类基本继承层数都是1,没有父子类的问题
  • ISP(接口分离原则): 三次作业不涉及接口问题
  • DIP(依赖倒置原则):三次作业中的类基本没有继承层次,所以也不涉及依赖倒置的问题

UML时序图分析


好了,我完事了

上面就是本系列作业的设计了,下面说一些我想说的

相比于上个系列作业,这个系列突出的需求增加更加明显,也更加友好,不存在说两次需求相互矛盾的情况,对于设计优秀的同学,每次作业的编码量肯定是不大的

然后吧,这个时间效率这个事情,我觉得确实是一个摸索的过程,群里有人在说用NN,问题不大,其实我上面说到的自己去尝试算法,然后调整算法,其实就是个手动NN的过程(误),真正要是在实际中解决这种鬼畜电梯的问题,就是要靠NN来实现根据过去推将来的,用NN来实现对未来的分析预测,从而调整自身调度策略,获得最优调度

下面想重点说一下这个监听者模式,老师上课提了很多次,下课也听同学们有讨论,所以说一点个人的见解(以下都是胡说,理性参考)

监听者模式的提出,是为了能够让一个个的listener动态地加入监听和退出监听,同时,在Commander新消息发布的时候,可以通知所有监听者,javaSwing库就大量使用了这种设计

针对本次作业,笔者最开始是考虑过用这个模式的,每当输入一个指令就通知一下电梯,然后电梯取指令,刚好很多电梯,然后可以都注册为listener

但是存在以下几个问题:

  • 所谓通知,在这里的体现就是把指令放进去,但是,在指令来的时候,电梯可能正处于上锁状态(例如,进出处理中),这时我们的Commander试图尝试往每个电梯里放指令,就会遇到锁
  • 其实上面那个还不是最重要的,毕竟只是要等一下下就好,问题是当某个请求无法被任何电梯处理的时候,这个请求就要被存起来,这样的话,其实就又退回了我们的生产消费模型了(毕竟在Swing中,如果一个消息没有监听器,这个消息是会被扔掉的,但是显然我们这里不能这样)

所以,这个模式虽然很美,但是放在本系列作业中也许有些不当之处,当然,这只是我浅薄的认识,如果大家有更好的实现方式,也欢迎在下面留言,一起讨论,一起学习

我永远爱着OOP——第二单元作业总结的更多相关文章

  1. OO第二单元作业总结【自我反思与审视】

    第二单元作业的完成史,就是一部心酸的血泪史…… 多线程的出现为我(们)打开一片广阔的天地,我也在这方天地摸爬滚打,不断成长!如果说第一单元之前还对Java语法有所了解的话,那么这单元学习多线程则完全是 ...

  2. BUAA OO 2019 第二单元作业总结

    目录 总 架构 controller model view 优化算法 Look 算法 多种算法取优 预测未来 多线程 第五次作业 第六次作业 第七次作业 代码静态分析 UML 类图 类复杂度 类总代码 ...

  3. 【BUAA-OO】第二单元作业总结

    第二单元作业总结 ——电梯恐惧症患者的极限自救 一.   第一次作业程序分析 1.     设计策略简略分析 线程:主线程.输入线程和电梯线程,另有一个持有请求队列的调度器,一个对输入进行处理的Req ...

  4. BUAA_OO第二单元作业总结——多线程

    OO第二单元作业总结——多线程 单元任务 本单元主要的内容是通过模拟电梯的运行来熟悉多线程的实现,从简单的单部FAFS电梯开始,ALS电梯,到最后的多部ALS电梯. 一.设计策略分析总结 1.1 多线 ...

  5. 【OO学习】OO第二单元作业总结

    OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...

  6. 电梯也能无为而治——oo第二单元作业总结

    oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...

  7. OO第二单元作业小结

    前言 转眼已是第九周,第二单元的电梯系列作业已经结束,终于体验了一番多线程电梯之旅. 第一次作业是单电梯的傻瓜调度,虽然是第一次写多线程,但在课程PPT的指引下,写起来还是非常容易:第二次作业是单电梯 ...

  8. 电梯模拟系统——BUAA OO第二单元作业总结

    需求分析 官方需求 本次作业需要模拟一个多线程实时多电梯系统,从标准输入中输入请求信息,程序进行接收和处理,模拟电梯运行,将必要的运行信息通过输出接口进行输出. 本次作业电梯系统具有的功能为:上下行, ...

  9. oo第二单元作业总结

    oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...

随机推荐

  1. BUAA_OO Summary——多项式求导问题

    从C.DS.计组一路折磨过来, 几乎都在采用过程化.函数式的编程思想.初接触面向对象的项目开发,经过了三周的对多项式求导问题的迭代开发,经历了设计.coding.测评环节,算是对面向对象有了一定的认识 ...

  2. python基础之函数式编程

    一.定义: 函数作为参数作用:将核心逻辑传入方法体,使该方法的适用性更广,体现了面向对象的开闭原则: 函数作为返回值作用:逻辑连续,当内部函数被调用时,不脱离当前的逻辑. 二.高阶函数: 1.定义:将 ...

  3. react-native 组件整理

    好早之前整理的部分组件,不全 怕丢

  4. Java 短信发送

    package com.test; import org.apache.commons.httpclient.Header;import org.apache.commons.httpclient.H ...

  5. Luogu P1381油滴扩展

    传送门 数据范围给的很小啊,n >= 0 && n <= 7,所以给了DFS生存的空间. 对于每一个油滴,可以说在它下一个油滴放置之前,当前的这个油滴的半径并不确定(但是对 ...

  6. 关于使用jquery对input中type为radio的标签checked属性的增加与移除

    需求:对radio的checked属性先消除然后进行重新设置: 初步方案: $("auForm input :radio[value='0']").removeAttr('chec ...

  7. Kotlin基础

    1.函数也是对象,可以作为参数和返回值 2.使用驼峰命名,尽量避免下划线 3.public函数应当有说明文档 4.lambda中花括号内前后都应该有空格 5.空值安全检查  var s: String ...

  8. emWin智能家居主界面设计,含uCOS-III和FreeRTOS两个版本

    第6期:智能家居主界面设计配套例子:V6-910_STemWin提高篇实验_智能家居主界面设计(uCOS-III)V6-911_STemWin提高篇实验_智能家居主界面设计(FreeRTOS) 例程下 ...

  9. Java 延迟队列使用

    延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任务放在该延时对列中,只有到了延时时刻才能从该延时对列中获取任务否则获取不到…… 应用场景比较多,比如延时1分钟发短 ...

  10. 高级Java面试总结1

    一.三大框架方面问题   1.Spring 事务的隔离性,并说说每个隔离性的区别 解答:Spring事务详解 2.Spring事务的传播行为,并说说每个传播行为的区别 解答:Spring事务详解 3. ...