2019-OO-第二单元总结

多线程电梯调度问题

思路综述

第一次作业

第一次作业是非常简单的傻瓜电梯,不需要考虑容量,不需要考虑调度策略,运用了基本的生产者消费者模型,而且生产者消费者模型也一直贯穿了三次作业。

第二次作业

按照指导书写了ALS调度方法,在elevator的run方法中,楼层每变化一次,都先判断电梯中是否有人(runlist),如果有人,则取第一个人为主请求,目的地即为这个人的tofloor,否则取电梯外的第一人(requestqueue),目的地即为这个人的fromfloor,根据得到的目的地判断电梯运行的方向(这里出现了问题,遗留到了第三次作业导致强测CPUt了QwQ)。然后在每层楼判断是否有目的地同方向的人,是否有要出去的人。最后电梯运行一层楼。

第三次作业

单部电梯的运行和第二次作业中基本一样, 除了限制了容量,在进人的时候需要加一个判断。本次作业的难点(对于不优化的我而言)应该是请求的拆分。请求的拆分,主要问题在于如何找到最短的拆分,和保持拆分后的时序,即一个请求的前半段拆分送达后,才能执行后半段拆分。对于前者我的实现是把三个电梯23层楼,类比成一个23*3的迷宫,然后用bfs来解决,与普通电梯不同的是,对于同一个电梯,即迷宫上的同一列,即使上下的两格标记为1(障碍,不可达),该电梯也是可以经过这一层的,只不过不可以在这一层换乘,故而判断条件有所变化。
上下移动:

左右移动:

对于后者,我创建了一个MyRequest类继承PersonRequest,多了一个state属性,当且仅当state为0的时候,这个请求可以被电梯接入,而当每个请求出电梯的时候,就遍历后面的请求,找到id一样的第一个请求,将它的state设为0。

代码分析

结构分析

第一次作业




整个程序都很简单,只有elevator的run方法数据偏高。

第二次作业




和第一次作业一样,程序思路比较简单,没有很复杂的方法,elevator的run方法数据偏高。

第三次作业



可以看到bfs方法的结构化程度、循环复杂度都遥遥领先,而Bfs类的类方法的平均循环复杂度也当仁不让地排在了第一,电梯线程类和调度器类的方法总循环复杂度也非常高。

bug分析

前两次作业没有什么bug,只不过第二次作业的优化性能分较低。第三次作业出现了CPU tle的问题,检查了很久很久,一直在while循环体里找,看是否发生了轮询,经过了漫长的printf大法之后,发现是一个惊天大bug,这样看来强测只炸两个点都是我欧。问题出现在Schedule类中的get方法,在原来的get方法中,我遍历整个队列,如果找到符合要求的,则返回该请求,找到null,则返回null,否则wait循环,但是wait被唤醒过后,我返回了requestqueue.get(0)……这简直毫无道理了,即会导致电梯跑到它不该到的楼层一直等待。
为解决这个问题,我把get方法分离出一部分即getresult方法,在这个方法里,如果符合要求,就返回符合要求的request的索引+1,null则返回-1,否则返回0,在get方法中,如果getresult方法的返回值为0,则等待,被notify后重新调用getresult方法,while 返回值为0,则一直循环,直到返回null或者符合要求的request。

风格分析

比较喜欢我的第二次作业的作业风格,在elevator类中,每一个方法都比较短,都有自己独立的功能,结构层次比较清晰,容易理解。而第三次作业就存在了哪里有问题给哪里加补丁的情况,显得非常丑陋,甚至还曾经把bfs的迷宫放在scheduler类里,不过最后还是单独为它构建了一个类。

Hack Hack Hack

这次互测还挺不爽的,这老哥简直不顾OS期中考试,仿佛定了闹钟一样精准提交测试样例,然而到最后一波修复就解决了……bug列表 从来没有这么长过。

当然我们在跑评测机的时候,很少会去研究对方的代码,检查是不是同质错误,但我觉得提交二三十次,狂刀别人的做法还是没有什么必要,不过的确监督了我找bug。

难点总结

线程安全

在这次作业中,出了很多线程不安全的bug,在debug的过程中才逐渐对线程安全和线程不安全有了一些认识。比如说经常出现的一个问题:IndexOutOfBoundsException异常,这是因为Arraylist是线程不安全的,而我又没有锁成功。
最后感谢晶巨分享的一篇文章,

我们观察发生越界时的数组下标,分别为10、15、22、33、49和73。结合前面讲的数组自动机制,数组初始长度为10,第一次扩容为15=10+10/2,第二次扩容22=15+15/2,第三次扩容33=22+22/2...以此类推,我们不难发现,越界异常都发生在数组扩容之时。
由此给了我想法,我猜想是,由于没有该方法没有同步,导致出现这样一种现象,用第一次异常,即下标为15时的异常举例。当集合中已经添加了14个元素时,一个线程率先进入add()方法,在执行ensureCapacityInternal(size + 1)时,发现还可以添加一个元素,故数组没有扩容,但随后该线程被阻塞在此处。接着另一线程进入add()方法,执行ensureCapacityInternal(size + 1),由于前一个线程并没有添加元素,故size依然为14,依然不需要扩容,所以该线程就开始添加元素,使得size++,变为15,数组已经满了。而刚刚阻塞在elementData[size++] = e;语句之前的线程开始执行,它要在集合中添加第16个元素,而数组容量只有15个,所以就发生了数组下标越界异常!

来源于https://blog.csdn.net/u010010428/article/details/51258783

请求拆分

请求拆分的方法有很多种,先后考虑了地铁线换乘的模型(上学期数据结构作业题),即用Dijkstra最短路径算法,和BFS迷宫模型两种方法。后来还是选择了BFS迷宫模型,写起来比较容易。

wait/notifyAll

在我写第三次作业之前都没有搞清楚notifyAll和wait的作用,加了很多notifyAll避免线程一睡不醒,但实际上并不需要,总的来说,scheduler的get方法里需要用wait,如果电梯暂时没有可以执行的请求,且没有读取到null的时候,就让它wait,在add request的时候要加notifyAll,唤醒wait的线程。

感想

总的来说感觉不是很理想,出现了面对中测编程的情况,没有主动去编一个评测机,以致于自己的bug也没有好好找,在hack别人的时候也显得很无力(尤其是被别人hack几十次想反击一波的时候),还有关于线程安全、死锁等问题也没有仔细思考,而是等问题出了再去找寻解决方案。相比第一单元而言,作业变复杂,而自己反而变得有些松懈,不太ok。

2019-oo-第二单元总结的更多相关文章

  1. oo第二单元作业总结

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

  2. OO第二单元优化博客

    OO第二单元优化博客 第五次作业没有性能分,但是,我在这一单元的宗旨就是写一个日常生活中 最常见的那种电梯,所以第五次我没有写傻瓜电梯,而是直接写了个\(look\),和第六次基本相同. 总计一下lo ...

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

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

  4. OO第二单元小结

    OO第二单元小结 一.三次作业代码分析. 1.第一次作业 第一次作业是单部电梯的傻瓜调度,由于其过分傻瓜,所以第一次作业我只有两个类,一个main,一个电梯,main类负责不断从输入流中读取命令,如果 ...

  5. OO第二单元多线程电梯总结

    OO第二单元多线程电梯总结 第一次作业 设计思路 Input为输入线程,负责不断读取请求并将读到的请求放入调度器中. Dispatcher为调度器,是Input线程和Elevator线程的共享对象,采 ...

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

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

  7. 2020北航OO第二单元总结

    2020北航OO第二单元总结 前言 本单元考察基于多线程的电梯调度问题,成功让我从一个多线程小白到了基本掌握了使用锁来控制线程安全的能力,收获颇多(充分体验了迷茫地de一个又一个死锁bug的痛苦). ...

  8. OO第二单元——多线程(电梯)

    OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...

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

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

  10. OO第二单元(电梯)单元总结

    OO第一单元(求导)单元总结 这是我们OO课程的第二个单元,这个单元的主要目的是让我们熟悉理解和掌握多线程的思想和方法.这个单元以电梯为主题,从一开始的最简单的单部傻瓜调度(FAFS)电梯到最后的多部 ...

随机推荐

  1. MariaDB:开启日志记录SQL

    1.开启日志 红色是命令,之下是回显. MariaDB [jksfrz]> SET GLOBAL log_output = 'TABLE'; Query OK, 0 rows affected ...

  2. Python-Django-BMS-增删改查

    无名分组 有名分组 反向解析 更改路由后不影响,动态传值 locals()传参 queryset.update() 自定义过滤器 {{forloop.counter}} new.authors.add ...

  3. matplotlib学习笔记

    1.简介 matplotlib是python的一个2D绘图库,它可以在不同平台上地使用多种通用的绘图格式(hardcopy formats)和交互环境绘制出出版物质量级别的图片.matplotlib可 ...

  4. 【java】Java组件概览(1)

    如上图所示,Oracle的Java SE8有两个产品:JDK和JRE.其中,JRE的内容包括图中①~⑤,它是JDK的子集. ⑥中的红色部分与JRE有重合. [参考] 1.https://docs.or ...

  5. 完整备份和差异备份数据库的SQL脚本

    工作中需要创建SQL Job对数据库进行定期备份,现把脚本记录如下. 1. 完整备份: -- FULL declare @filename varchar(1024), @file_dev varch ...

  6. SQL 概述

    SQL是用于在数据库中存储,操作和检索数据的标准语言. 本教程教你如何使用SQL:MySQL,SQL Server,MS Access,Oracle,Sybase,Informix,Postgres和 ...

  7. 金蝶K/3 审批相关SQL语句

    金蝶K/3 审批相关SQL语句 --http://127.0.0.1/lightApp/todocheckTask.aspx?AccID=84&&FClasstypeID=1071&a ...

  8. (三)Knockout 控制流程

    foreach 示例1:迭代数组 foreach binding主要作用于lists或是tables内数据单元的动态绑定.下面是一个简单的例子: <table> <thead> ...

  9. [CF662C] Binary Table(FWT)

    题意: https://www.cnblogs.com/cjyyb/p/9065801.html 题解:

  10. python之基于libsvm识别数字验证码

    1. 参考 字符型图片验证码识别完整过程及Python实现 2.图片预处理和手动分类 (1)分析图片 from PIL import Image img = Image.open('nums/ttt. ...