你电梯没了—OO第二单元作业思考

时间:2022-09-06 11:14:52

写在前面


这三次电梯调度作业,主要是学习多线程并行操作,对于各个线程的时间轴的把握,互相的配合与影响,通过使用锁来解决访问冲突等方面。

个人在学会Thread相关操作之外,写出来一些奇怪结构的诡异操作,而这些操作是在已有方法学习不精的情况下意外获得,虽然后期证明效率不高,但也是扩宽了代码思路,将在下文一一说明。

另外有一些经验教训作为后鉴。


作业要求与代码结构


第一次作业

作业要求:单部单人电梯,目的选层调度,一到十五层运行。

代码结构:

你电梯没了—OO第二单元作业思考

  此次作业我写的极其简单只有六十余行,同时拓展性也几乎为0。

  由于第二次作业结构仍然简单故而继承此次的结构和重构相比耗费时间相差不大。

  说是多线程结构,其实只有电梯线程和main线程,同时main线程也只有三行:

Elev e1 = new Elev();
Thread ele1 = new Thread(e1);
ele1.start();

  故而其实可以看做只有一个电梯线程,由于只有一个电梯,索性将输入部分直接放在唯一的电梯里,线程的核心即输入队列,电梯从队列里逐次取出请求并执行,直至收到null指令线程死亡,程序结束。

复杂度分析:

  由于是在完全舍去代码复用打算情况下写出的极度简单的结构,使复杂度很低没有分析价值,故略去。


第二次作业

作业要求:单部可捎带无限人数电梯,目的选层调度,负三到负一,一到十五层运行。

代码结构:

你电梯没了—OO第二单元作业思考

  到这时,输入请求的时间与电梯的运行便有了关系,不再像上次一样无影响。

  故而将输入提出,另创建一个线程。

  核心变为了两个队列-电梯外请求队列与电梯内包括主请求与捎带请求的队列。

  电梯空闲时确定外队列第一个请求为主请求,每变化一层判断一次是否存在可捎带请求,当请求者进入电梯时,请求由外队列转入内队列,出电梯时,内队列将其去除。

复杂度分析:

你电梯没了—OO第二单元作业思考

  明显可见这些方法复杂度都很高,主要原因可能是第一次架设的结构过于简单,功能少类也很少,后来不断改进过程中没有加入新的类,而是对已有方法不断扩展导致。checkin与checkout方法在电梯每移动一层都要判断两次,这是一个很大的问题,当时的情况是分析一次也可行,但是代码量要增加不少,于是决定牺牲效率(x)


第三次作业

作业要求:三部可捎带不同人数电梯,目的选层调度,限制不同层数运行。

代码结构:

你电梯没了—OO第二单元作业思考

你电梯没了—OO第二单元作业思考

你电梯没了—OO第二单元作业思考

  此次作业真实地感受到了什么是多线程。除了Main之外,设置了输入线程、电梯请求分配器线程以及三个独立的电梯线程。

  程序核心由第一次的一个队列到第二次的两个队列,这次变成了七个队列——输入请求队列,已分配给某电梯的电梯外队列*3,电梯内队列*3。

  另外队列的存储对象不再是前两次的PersonRequest类,而是在此基础上拓展加入两个新元素——RealFrom和RealTo的PRplus类(名为Rpp)。

  分配器线程从输入队列中提取请求,根据三部电梯本身的性能、停靠楼层、目前状态把请求分配给三部电梯,该条Rpp转移到相应电梯的电梯外队列,接下来对每个电梯而言,拥有内外两个队列这与上次单电梯的情况基本相同,执行操作规则也基本不变。

  最大的变化是换乘的情况,请求在分配器分配给电梯外队列时便已经决定是否换乘,如何换乘。此时将换乘站存入RealTo,当到达该楼层时,将RealTo(也即当前楼层)存入RealFrom,将请求原本的To存入RealTo,最后将该指令从电梯内队列再转移至输入总队列,进入分配器。

  当分配器收到null指令时,不断判断三组电梯内外队列是否为空,当全空时,程序结束。

复杂度分析:

你电梯没了—OO第二单元作业思考

  首先是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以及锁时它们的互相干扰。

    不处理

直接建立三个电梯线程类然后每个类创建一个实例对象,反正代码量也不多,重复也不会有多大影响。

你电梯没了—OO第二单元作业思考  然后事实证明他们之间并不会互相干扰,当我把三个类换成一个类三个实例时,电梯仍然正常运行。所以说不用预防过度,代码的*度比想象中的要高,没有那么多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第二单元作业思考的更多相关文章

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

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

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

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

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

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

  4. oo第二单元作业总结

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

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

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

  6. OO第二单元作业——魔鬼电梯

    简介 本单元作业分为三次 第一次作业:第一次作业要实现单部简单电梯,停靠所有楼层,无载客容量,性能分考量电梯运行时间. 第二次作业: 第二次作业实现多部电梯,电梯数量由初始化设定,每部电梯都停靠所有楼 ...

  7. OO第二单元作业分析

    前言 这一单元关于线程安全的作业结束了,在助教提供的接口的帮助以及老师提供的设计模型的指导下,这三次作业还是相对轻松地完成了,中间也没有出现什么bug,可能就是因为简单的逻辑不容易出错吧,可惜两次都由 ...

  8. OO第二单元作业小结

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

  9. 北航OO第二单元作业总结(2.1~2.3)

    在经过第一单元初步认识面向对象编程思想后,本蒟蒻开始了第二单元--多线程部分的学习.本单元的作业是构造符合条件的"目的选层电梯"模型,自行设计调度算法,进行合理调度,完成所有乘客的 ...

随机推荐

  1. db2 游标使用

    游标一般用来迭代结果集中的行 为了在一个过程中处理一个游标的结果,需要做以下事情: 在存储过程块的开头部分 DECLARE 游标. 打开该游标. 将游标的结果取出到之前已声明的本地变量中(隐式游标处理 ...

  2. Favorite Games

    Samurai II: Vengeance: http://www.madfingergames.com/games

  3. EF CodeFirst增删改查之‘CRUD’

    最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精    本篇旨在学习EF增删改查四大操作 上一节讲述了EF ...

  4. Browser设置搜索引擎

    Browser设置搜索引擎,在com.android.browser.preferences.GeneralPreferencesFragment中加载R.xml.general_preference ...

  5. maven自动化部署插件sshexec-maven-plugin

    在maven pom.xml 文件plugins里增加               <plugin>                 <groupId>com.github.g ...

  6. Converting a &period;jks Key Store to a &period;pem Key Store

    In order to convert a Java key store into a Privacy Enhanced Mail Certificate, you will need to use ...

  7. Redis实现分布式锁与任务队列

    Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...

  8. 个人博客制作如何选择前端模板 thinkcmf后台加载新模板 CSS js文件

    我们的博客后台已经搭建好了,接下来我就要选择一个合适的模板做自己的博客,首先要定位你的博客是做什么用的,是属于什么行业,根据自己博客的定位选择适合的模板. 如果你是设计师,又会前端设计开发,那就可以自 ...

  9. 阿里云配置ssl证书服务遇到的几个问题和解决方法

    系统环境: 系统:阿里云ECS CentOS6.5+Apache2.4.10 前提:公司需要将站点升级到使用SSL证书服务(https) 实践执行:在阿里云的证书服务--选择了一个免费的证书服务,毕竟 ...

  10. &lbrack;javascript-debug-ajax-json&rsqb;两种不同的json格式数据

    Bug 1: 1. 这里面的 data 只是一维数组{"state":0,"errorCode":0,"data":{"origi ...