OO_2019_第二单元总结——多线程电梯

  传说中的多线程(魔鬼)电梯完成啦!

一、程序设计分析与基于度量的程序结构分析

  三次电梯都统一地采用了生产者-消费者模型,每次在前一次的基础上进行添加,没有大规模的重构,可以说设计含有一定的可拓展性。

  第一次电梯(单部多线程傻瓜调度(FAFS)电梯)

  这是第一次接触多线程,十分迷惑。起初实在不知道什么是多线程,甚至写了单线程版本。后来通过查资料和多次尝试,根据生产者-消费者模型探索出了主线程(RequestInput)负责管理输入请求并存入Queue,另一个线程(ElevatorRun)模拟电梯的行为,从Queue中一次取出一条请求,根据规则将人送到目的楼层的模式。

  我采用的是对请求队列的方法加锁的方法来保证线程安全。

  还有一个比较主要的问题就是如何让电梯能够开始运行并且稳定地结束:在Queue中设置了flag属性,当没有取到指令时置为1,如果flag为1且请求队列为空则结束电梯,这样就可以保证电梯稳定结束运行。

  因为是傻瓜调度,所以省略了调度器,直接在ElevatorRun的run() 方法中让电梯运行并且输出,这也就导致了该方法复杂度较高,应将输出信息和调度电梯都分离出不同的方法,提高模块化程度。

  第二次电梯(单部多线程可捎带调度(ALS)电梯)

  第二次电梯增加了捎带功能,使问题的复杂度上升了一个台阶。整体结构与第一次一致,但是没有真正理解设置调度器的用处,因此仍然在电梯类里面进行了取指令、分析捎带、电梯运行、输出结果这一系列工作,导致了出现了较为严重的bug。

  在修复bug的过程中,我渐渐理解调度器是应该分析请求并给电梯传递消息,然后电梯根据这些消息去运行。

  捎带策略:每次先去到主请求所在楼层,在送主请求的路上,到每一层判断是否当前楼层有人和主请求运行方向一致,如果有就让该请求进入电梯并且更新主请求目的楼层(目的楼层为最远楼层),当主请求到达目标楼层再进行新一轮循环。该方法只实现了最简单的捎带。在去接主请求的路上捎带,和不同方向的乘客的捎带都是可以优化的方向。

  PS:其中同一楼层多人上下以及乘客先上还是先下是需要细致分析的,稍有不慎就可能有人被关在电梯里啦。

  因为没有解耦所以Elevator和其中的run()方法还是负担过重,给程序带来了存在bug的风险。

  第三次电梯(多部多线程智能(SS)调度电梯)

  第三次是传说中最魔鬼的电梯:三部电梯停靠楼层不同,魔鬼3层要换乘,每部电梯还有不同的轿厢容量和运行时间。。。OMG,但后来经过将复杂问题简单化处理,可以转化为之前电梯的升级版(设计最重要!避免重构!)

  采用的策略:在调度器里面设置了三个请求队列,每次ReqInput取到一个指令就放入Dispatcher里面按照一定的换乘规则分成能够直达的请求,然后放入相应的电梯请求队列,在main() 方法中创建三个电梯,它们有不同的属性,每个电梯根据相应的调度器运行,捎带策略与第二次电梯一致。

  优点是容易实现,简洁清晰,不需要重构;缺点是分配请求机械化,性能不优。

  本次作业已经尝试大量降低耦合,但复杂度高的是run()方法和判断捎带的方法,这也是不可避免的,争取以后通过实践能继续提高结构化程度。

三、程序bug分析

  在三次电梯作业中,第一次简单电梯和第三次多部智能电梯没有在强测和互测中被发现bug,第二次捎带电梯强测中被发现五个同质bug。

  出现bug的原因:设置了请求队列类Queue,其中包括getone()方法,从队列中取出特定元素,并将其从队列中删除,如下

synchronized PersonRequest getone(int i) {
        PersonRequest temp = queue.get(i);
        queue.remove(i);
        length--;
        return temp;
    }

而在判断当前楼层是否需要捎带时,又使用Queue中的getqueue()方法,将请求队列直接取出,如下

synchronized ArrayList<PersonRequest> getqueue() {
        return queue;
    }

当发现该请求需要捎带时,在Elevator中使用getone()方法取出,但是此时Elevator中取出的队列没有更新。

bug修复:将遍历过程放入Queue中,建立passreq(int floor, int ud)方法,可以保证请求队列的同步更新,如下

synchronized PersonRequest passreq(int floor, int ud) {
        PersonRequest per = null;
        if (queue.size() != 0) {
            for (int i = 0; i < queue.size(); i++) {
                if (queue.get(i).getFromFloor() == floor &&
                        ud == judgedrc(queue.get(i))) {
                    per = queue.get(i);
                    queue.remove(i);
                    length--;
                    break;
                }
            }
        }
        return per;
    }

  出现问题的根本原因是对象之间没有降低耦合。调度器对象中存储请求队列,一系列和调度有关的功能都应该由调度器完成,否则就可能造成线程控制和线程安全方面的问题。而多线程一旦出现这种控制类的bug不容易自己检查出来,后果十分严重。

四、发现别人bug采用的策略

  第一次大家的程序都没有什么问题,之后的两次借用了同学分享的可以定时投放的测试工具,构造了请求数较多,情况较复杂的数据,发现了一些电梯调度问题和不能稳定结束的问题。非常感谢同学的分享,希望自己以后也可以做类似的测试工具。

五、总结

  这一单元主要学习了多线程问题,较为复杂且不容易理解,但多线程在现实生活中是十分有应用价值的,应该继续通过应用加深理解。如何保证线程安全是十分重要的问题,可以采用几种加锁方式,每种方式都有其特点,还要避免死锁。

  这一单元作业都采取了相对简洁清晰的思路完成,代码实现较为容易,但是相对性能不高,还应该在保证正确性的基础上探索优化性能,不能得过且过。

  设计注意可扩展性可扩展性可扩展性!

  多思考 继续加油!

原文地址:https://www.cnblogs.com/peggyhss/p/10753457.html

时间: 2024-10-12 03:30:31

OO_2019_第二单元总结——多线程电梯的相关文章

OO第二单元单元总结

总述 OO的第二单元主题是电梯调度,与第一单元注重对数据的输入输出的处理.性能的优化不同,第二单元的重心更多的是在线程安全与线程通信上.这此次单元实验之前,我并未对线程有过了解,更谈不上“使用经验”,整体上第二单元三个实验也做的较为吃力.三次实验,也算是对线程的一步步入门吧,以及由于对于线程不是很熟悉,所以我总是下意识的把程序分割,尽量减少通信量. 第一次实验 基本需求:单电梯.无捎带要求.基本无性能要求. 基本实现:一个主线程或者一个主线程与一个电梯线程 我采用的方法是使用一个主线程,因为实际

面向对象第二单元训练总结

一.前言 第二单元的三次作业是很有特点的三次作业.多线程电梯的设计思路和前两次电梯作业迥然不同,导致我花费了大量的时间去重构之前的代码,使其适应多线程电梯的作业要求:文件监视器是一个独立的作业,不像电梯和出租车那样是一个系列,因此写起来没什么包袱,感觉并不困难:出租车调度和多线程电梯写起来感觉比较相似,但出租车几乎没有算法上的难度,因此主要的工作都花费在了如何构建一个好的设计上面.这三次作业之间看起来没有什么关联,但却环环相扣,一步一步加深着我对多线程编程的理解. 我对这三次作业的总体难度评价为

BUAA_OO 第二单元总结

BUAA_OO 第二单元总结 写在前面 ? 多线程(multi-threading)是指从软件或硬件上实现多个线程并发执行的技术.现代处理器普遍具有多核的特点,支持同一时间执行多个线程,使用多线程技术可以提高程序并发度,整体提高处理性能.因此掌握多线程程序设计技术是CS学习必不可少的一部分. ? 多线程程序设计包括线程协同控制.线程安全保证以及线程程序设计模式等.本文主要总结OO课程第二单元所学的有关多线程程序设计的知识,实践体会,以及技术注记,以备后用. 第五次作业 ? 第五次作业实现了一个单

多线程电梯设计的总结与反思

一.FAFS电梯设计 这是第一次使用java多线程,主要的问题主要集中在两个方面 1.共享资源的数据同步 2.整体架构 先考虑第一个问题: 数据同步的问题显然可以使用synchronized解决,也就是经典的生产者消费者模型. 但是由于初次接触,对锁机制理解不清,我还探索了一种不那么好的方法——volatile,具体使用方法见Whai is volatile 它显然能用在这个问题的解决,但是其实volatile带来的问题很多,比如性能损失,也不具很好的普适性. 第二个问题: FAFS在调度策略的

第二单元总结

######第二单元###### #########################linux系统结构####################### 1. linux系统结构是倒树型 2. /bin                ##二进制可执行文件也就是系统命令 /sbin               ##系统管理命令存放位置 /boot               ##启动分区,负责系统启动 /dev                ##设备管理文件 /etc                #

第二单元练习题

<<<第二单元练习>>> 1.用命令和正则表达式按照要求建立文件 *)用一条命令建立12个文件WESTOS_classX_linuxY(X的数值范围为1-2,Y的数值范围为1-6) *)这些文件都包含在root用户桌面的study目录中 *)用一条命令建立8个文件redhat_versionX(x的范围为1-8) *)redhat_virsionX这些文件都包含在/mnt目录中的VERSION中 2.管理刚才信建立的文件要求如下 *)用一条命令把redhat_versi

MOOC 面向对象软件开发实践之基本技能训练 第二单元作业

这几天期末考试周  bolg 比较忙 停更了几天 晚上还是抽空把金老师第二单元的作业给做了 基本功能都实现  对象多对多信息交换  对象序列化  等等内容 都一一实现了 确实对初学OOP的人来说 很有帮助  代码就不给了 很简单 自己努力google 或者留言你的问题

第二单元. &nbsp; 简要的正则表达式及文件的移动复制

第二单元 1.用命令和正则表达式按照要求建立文件 *)用一条命令建立12个文件WESTOS_classX_linuxY(X的数值范围为1-2,Y的数值范围为1-6) *)这些文件都包含在root用户桌面的study目录中 [[email protected] Desktop]# touch study/WESTOS_class{1,2}_linux{1..6} [[email protected] Desktop]# mv study/WESTOS_class{1,2}_linux{1..6}

.C#认证考试试题汇编:第一单元:1,11 第二单元:1,11

第一单元1,11 好久没用异或都快忘了,就让我们一起来了解哈啥子事异或 说的这个,就不经意让我想起书上的几种交换值得方法了 我这儿说的交换的方法是,不使用第三个变量来交换,而是两个 实现条件是C a=100,b=10;第一种 缺点可能会如果超出范围溢出a=a+b; a=110,b=10b=a-b; b=100,a=110a=a-b; a=-10,b=100b=100,a=10;第二种 缺点可能会如果超出范围溢出a=a*b;a=1000,b=10b=a/b;b=100,a=1000a=a/b;a=