你电梯没了—OO第二单元作业思考
写在前面
这三次电梯调度作业,主要是学习多线程并行操作,对于各个线程的时间轴的把握,互相的配合与影响,通过使用锁来解决访问冲突等方面。
个人在学会Thread相关操作之外,写出来一些奇怪结构的诡异操作,而这些操作是在已有方法学习不精的情况下意外获得,虽然后期证明效率不高,但也是扩宽了代码思路,将在下文一一说明。
另外有一些经验教训作为后鉴。
作业要求与代码结构
第一次作业
作业要求:单部单人电梯,目的选层调度,一到十五层运行。
代码结构:
此次作业我写的极其简单只有六十余行,同时拓展性也几乎为0。
由于第二次作业结构仍然简单故而继承此次的结构和重构相比耗费时间相差不大。
说是多线程结构,其实只有电梯线程和main线程,同时main线程也只有三行:
Elev e1 = new Elev();
Thread ele1 = new Thread(e1);
ele1.start();
故而其实可以看做只有一个电梯线程,由于只有一个电梯,索性将输入部分直接放在唯一的电梯里,线程的核心即输入队列,电梯从队列里逐次取出请求并执行,直至收到null指令线程死亡,程序结束。
复杂度分析:
由于是在完全舍去代码复用打算情况下写出的极度简单的结构,使复杂度很低没有分析价值,故略去。
第二次作业
作业要求:单部可捎带无限人数电梯,目的选层调度,负三到负一,一到十五层运行。
代码结构:
到这时,输入请求的时间与电梯的运行便有了关系,不再像上次一样无影响。
故而将输入提出,另创建一个线程。
核心变为了两个队列-电梯外请求队列与电梯内包括主请求与捎带请求的队列。
电梯空闲时确定外队列第一个请求为主请求,每变化一层判断一次是否存在可捎带请求,当请求者进入电梯时,请求由外队列转入内队列,出电梯时,内队列将其去除。
复杂度分析:
明显可见这些方法复杂度都很高,主要原因可能是第一次架设的结构过于简单,功能少类也很少,后来不断改进过程中没有加入新的类,而是对已有方法不断扩展导致。checkin与checkout方法在电梯每移动一层都要判断两次,这是一个很大的问题,当时的情况是分析一次也可行,但是代码量要增加不少,于是决定牺牲效率(x)
第三次作业
作业要求:三部可捎带不同人数电梯,目的选层调度,限制不同层数运行。
代码结构:
此次作业真实地感受到了什么是多线程。除了Main之外,设置了输入线程、电梯请求分配器线程以及三个独立的电梯线程。
程序核心由第一次的一个队列到第二次的两个队列,这次变成了七个队列——输入请求队列,已分配给某电梯的电梯外队列*3,电梯内队列*3。
另外队列的存储对象不再是前两次的PersonRequest类,而是在此基础上拓展加入两个新元素——RealFrom和RealTo的PRplus类(名为Rpp)。
分配器线程从输入队列中提取请求,根据三部电梯本身的性能、停靠楼层、目前状态把请求分配给三部电梯,该条Rpp转移到相应电梯的电梯外队列,接下来对每个电梯而言,拥有内外两个队列这与上次单电梯的情况基本相同,执行操作规则也基本不变。
最大的变化是换乘的情况,请求在分配器分配给电梯外队列时便已经决定是否换乘,如何换乘。此时将换乘站存入RealTo,当到达该楼层时,将RealTo(也即当前楼层)存入RealFrom,将请求原本的To存入RealTo,最后将该指令从电梯内队列再转移至输入总队列,进入分配器。
当分配器收到null指令时,不断判断三组电梯内外队列是否为空,当全空时,程序结束。
复杂度分析:
首先是both和belong方法和上次一样,为了减少代码而使分析次数变多,这两个方法是让分配器把请求具体分配给某电梯用的。每个请求belong比执行八次,若换乘则执行both六次,故而复杂度极高。
checkin与checkout和上次一样,具体见下文所说,我最大程度的复用了第二次的代码,这两个方法完全没有改动……
所遇问题与奇怪的解决方案
当请求队列为空时,电梯线程如何暂停等待。
这个问题现在看来很简单,大概就是用wait();,notify();,但由于第一次作业时,对这个方法运用不熟练导致怎么写都一堆bug,不是不运行就是运行起来不停止。当时的想法是,为什么不能直接无限sleep呢,无法预知sleep时间没关系,用循环就可以。
while (prilist.isEmpty() == true) {
if (free == true && inglist.isEmpty() == true) {
System.exit(0);
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
用while判断,当队列为空时就sleep(1);由于精密度问题,在程序执行的过程中效率影响并不大,而且代码比wait()简单易理解,也没有什么BUG(至少在弱中强测、互测、自我反思中没有发现)。
如何处理三个电梯时,对电梯创建三个实例时,使用wait、notify以及锁时它们的互相干扰。
不处理
直接建立三个电梯线程类然后每个类创建一个实例对象,反正代码量也不多,重复也不会有多大影响。
然后事实证明他们之间并不会互相干扰,当我把三个类换成一个类三个实例时,电梯仍然正常运行。所以说不用预防过度,代码的自由度比想象中的要高,没有那么多bug。
既然二三次作业最大的差别就是电梯的数量,那么如何直接使用第二次作业的全部代码?
个人感觉这两次作业是在这六次作业中,差别最小的两次。将一个电梯线程改为三个之后,电梯本身的各种性质则不算是差别,唯一的差别在于换乘功能。
所以待解决的问题只有两个:三个电梯的竞争问题、换乘问题。
三个电梯的竞争问题只需要加入一个分配器线程,在输入线程和电梯线程之间加入一个缓冲,或者说分流工具。在分配器中决定各个请求的归属。
换乘问题的解决则是通过改变PersonRequest类,具体见上文第三次作业代码结构部分。
然后我们看到除了新加入的这两个模块之外,原有的三个类——main本来就跟空白没什么区别,输入类一行没变,电梯类分别改了各自的速度(一行)、添加了可停靠判断(两行)、当换乘时改变RealFrom、RealTo再把请求转入输入队列(四行)。也就是说第三次的程序仅仅改变了第二次作业不到十行的行有意义代码,加入了两个新类(其中一个只有三行)。感觉已经是很大程度的复用了。
BUG问题
本单元debug思路与上单元差别很大 ,上次针对他人的代码,主要是大量实例的尝试,少量对代码的分析针对代码查找bug。而这单元我完全是将别人的代码完全理解之后有所针对地进行尝试,没有不看代码直接构建极端实例的情况。
这种变化带来的影响则是耗用大量时间,但是学到了很多新奇思路,为当前作业的下一次做了很好的铺垫。
具体来说,
第一次作业,由于代码结构、功能极其简单,只有在弱测中出现了开关门时机问题,而强测、互测中并未发现问题。
第二次作业,出现了电梯调度方案的问题,由于某些情况发生时的效率低下而超时。比如就我的代码而言,电梯运行一个循环结构为:
if (last.getFromFloor() != cufl) {
emmoveto(last.getFromFloor());
}
TimableOutput.println("OPEN-" + cufl);
Thread.sleep(400);
letin();
TimableOutput.println("CLOSE-" + cufl);
moveto(last.getToFloor());
while (inglist.isEmpty() == false) {
last = inglist.get(0);
moveto(last.getToFloor());
}
即分为三个阶段,电梯运行至起始楼层并进人,电梯运行至目标楼层,若电梯中还有人则继续运行到改请求的目标楼层。
由于仅仅分了三个阶段,导致第一个阶段捎带策略与二三不同,二三是只要方向相同都可以捎带,而第一个阶段则是方向相同的同时必须被捎带者在到达主请求起始楼层前下电梯才可以捎带。就因为这个限制,导致效率要求不足而超时。
第三次作业,在写完之后各种情况分析及优化之后,弱中测全部通过的情况下,竟然没有进入互测!等强测结果出来后发现二十个点全部WA,分析代码发现是第三个电梯可停靠楼层抄错了,导致出现了弱中测全过而强测全挂的奇怪情况。当我把楼层改过之后过了17个点,剩下三个点又是和第二次作业bug类似的效率问题。
经验教训与反思
- 虽然说代码复用是件高效的事,但也不能过于追求复用,毕竟需求有所改变,原来的结构有很多相对低效的应该更换。
- 代码复杂度和结构复杂度(程序效率)的平衡应该掌握好,不能为了轻松而经常写简单的结构,毕竟过于粗糙而且容易产生bug。
- 思考特别方法、奇怪的结构是有一定意义的,但不应该是在已有方法没掌握好情况下,不认真学明白已有方法而寻求捷径。
- 反思代码结构并进行优化、审查极端情况下的BUG固然重要,但更重要的是基层架构没有问题,不会出现很明显的基本错误(指楼层数抄错……)。
你电梯没了—OO第二单元作业思考的更多相关文章
- 电梯模拟系统——BUAA OO第二单元作业总结
需求分析 官方需求 本次作业需要模拟一个多线程实时多电梯系统,从标准输入中输入请求信息,程序进行接收和处理,模拟电梯运行,将必要的运行信息通过输出接口进行输出. 本次作业电梯系统具有的功能为:上下行, ...
- 电梯也能无为而治——oo第二单元作业总结
oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...
- 【OO学习】OO第二单元作业总结
OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...
- oo第二单元作业总结
oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...
- OO第二单元作业总结【自我反思与审视】
第二单元作业的完成史,就是一部心酸的血泪史…… 多线程的出现为我(们)打开一片广阔的天地,我也在这方天地摸爬滚打,不断成长!如果说第一单元之前还对Java语法有所了解的话,那么这单元学习多线程则完全是 ...
- OO第二单元作业——魔鬼电梯
简介 本单元作业分为三次 第一次作业:第一次作业要实现单部简单电梯,停靠所有楼层,无载客容量,性能分考量电梯运行时间. 第二次作业: 第二次作业实现多部电梯,电梯数量由初始化设定,每部电梯都停靠所有楼 ...
- OO第二单元作业分析
前言 这一单元关于线程安全的作业结束了,在助教提供的接口的帮助以及老师提供的设计模型的指导下,这三次作业还是相对轻松地完成了,中间也没有出现什么bug,可能就是因为简单的逻辑不容易出错吧,可惜两次都由 ...
- OO第二单元作业小结
前言 转眼已是第九周,第二单元的电梯系列作业已经结束,终于体验了一番多线程电梯之旅. 第一次作业是单电梯的傻瓜调度,虽然是第一次写多线程,但在课程PPT的指引下,写起来还是非常容易:第二次作业是单电梯 ...
- 北航OO第二单元作业总结(2.1~2.3)
在经过第一单元初步认识面向对象编程思想后,本蒟蒻开始了第二单元--多线程部分的学习.本单元的作业是构造符合条件的"目的选层电梯"模型,自行设计调度算法,进行合理调度,完成所有乘客的 ...
随机推荐
- Linux 系统状态检测命令
介绍快速查看Linux系统运行状态的能力(网络网卡.系统内核.系统负载.内存使用情况.启用终端数量.历史登录记录.命令执行记录.救援诊断)等命令使用方法 1.ifconfig 用于获取网卡配置和网络 ...
- 时效性:NABCD分析结果
N:失物招领,表白墙,二手跳骚群,里面的信息都没有真正的利用起来,好多有用的信息,全部被覆盖,同时,也有好多信息,使用户不想看到的,时效性,是个重大的问题. 例如:暑假放假,我背着书包拿着行李,等候火 ...
- 注意source folder与folder是不同的,避免404错误
在整合ssm框架的时候,程序和配置文件都没写错,tomcat也部署成功了,但在访问的时候一直404,web项目自带的index.jap却能正常访问,一直找不到原因,后来发现建立放配置文件的文件夹con ...
- C# ADO.NET中设置Like模糊查询的参数
ADO.NET进行参数化时会自动将参数值包含在单引号中,除了特殊需求,最好不要自己手动添加单引号.ADO.NET中识别参数标识是使用符号@,如果在SQL语句中将参数标识放在单引号中,单引号中的参数标识 ...
- CentOS安装svn(subversion)版本管理
yum -y install subversion svnadmin create /svn/repository cd /svn/repository/conf ll 查看文件-rw-r--r-- ...
- PHP SoapClient 调用与鉴权,以及对Java和C# 的webservice的兼容处理
SoapClient使用注意事项: 第一要加上 cache_wsdl参数,以防服务器调用的是缓存的wsdl文件 然后是参数传递,如果是使用PHP自己写的WebService,参数传递按正常方式即可 1 ...
- 一个linux 驱动升级的小问题记录
重复踩了两次坑,所以简单记录下. 内核 3.10. 在修改了驱动的gro实现之后,进行驱动版本的升级,make && make install 之后,发现tg3的驱动,没有生效. 相同 ...
- 【396】python 递归练习题(COMP9021)
Merging two strings into a third one Say that two strings s1 and s2 can be merged into a third strin ...
- 针对Oracle数据库中SCOTT方案的多表查询的一个例子
查询出每个员工的姓名.职位.月薪.所属部门名.月薪等级及其领导的姓名.职位.所属部门名.月薪等级.select e.ename 员工姓名,e.job 员工职位,e.sal 员工月薪,d.dname 员 ...
- ARTS打卡计划第一周-Tips-ControllerAdvice的使用
通常在开发具体项目过程中我们可能会面临如下问题: 统一所有的json返回结果 统一处理所有controller中的异常,并且给不同异常不同的返回状态值 统一对返回的接口做数据校验或者加密,防止篡改 在 ...