BUAAOO第二单元总结之电梯问题
---恢复内容开始---
Homework1 傻瓜电梯
程序架构
第一次题目非常简单,思考也非常简单,一部电梯傻瓜调度。将命令入公共的队列,电梯从公共队列中取命令即可,其中只需要使用ArrayBlockingQueue就可以使线程安全,并不需要加上锁,因为只有这个队列需要公共访问修改,使用阻塞队列即可。
同步控制
第一次作业结构简单,只需要使用ArrayBlockingQueue就可以实现同步控制。
BUG的出现
1.使用电梯取队列命令时,若也使用阻塞取,就可能使整个线程阻塞而无法退出,因此我是用poll进行取。
2.程序退出不能仅仅判定输入为NULL,还得注意命令队列是否为空,不然将导致命令还没执行完就退出。
Homework2 捎带电梯
程序架构
这一次作业对电梯的架构做了很大的调整。它与第一次是完全不同的,因为第一次过于傻瓜,其实整个电梯类并不是电梯,而是一个运算器,对from和to两个floor进行运算时间即可,本质上并不是电梯。
所以首先我得明确什么是捎带,什么是电梯,捎带就是电梯仍然就是这么去接人,只不过接人的路上可能可以捎带上别人,它的接人方向和放人方向始终没有任何变化,因此我就将电梯的属性做了很大的调整,楼层的概念变得没那么重要,而方向作为主要属性,接人方向,放人方向和接人的极限层(即从接人任务结束的那一层)作为电梯每次开始运作的基础,然后每一层再给它分配任务,而不对这三个属性做任何变化。
比起第一次的傻瓜电梯,多加了一个调度器类,同时将请求队列放在了调度器中(调度器并不是一个线程,只是一个普通类,在电梯要调用时调用)。电梯多了outelelist和inelelist两个数组,用于存储在电梯外等候的人和在电梯里的人。电梯以pick和put为一次流程,每一次流程给电梯设置pick的方向和put的方向,以及pick的楼层,然后电梯开始运行,每当运行到一个楼层(初始层也算),就给电梯分配可以捎带的任务并且加入到电梯的outelelist,并且检索outelelist是否有可以进入电梯的,有就加入inelelist(等于人进电梯),并检索inelellist检索是否有达到楼层的,有就从inelellist移除(等于人出电梯),这些操作就完成了这一层的任务,并继续运行直到pick和put结束。
同步控制
虽然第二次设计复杂了很多,但是其实被线程共享的数据只有主请求队列,而电梯内部的两个接人放人数组都是私有的,只为电梯线程使用,因此不需要控制,所以我主请求队列仍然使用阻塞队列即可。
BUG的出现
1.起初我数组遍历使用下标从小到大进行遍历并且检索到就用数组的remove,这就会导致当我remove一个元素之后使size变化而下标i继续增长,某些元素无法检索到,解决办法就是从大到小遍历。
2.在调度器每次给电梯设置pickdirection时,将正确的direction传递给电梯,然而调度器内部记录的direction没有变化,导致会增加一些不能经过的捎带任务,从而使电梯不断爬楼却无法到达捎带楼层而无限爬楼。
Homework3 捎带电梯+多部不同的电梯
程序架构
复杂度控制还行
电梯类
电梯和调度器的时序图
类的数量并没有变化,依然是电梯类,调度器类,输入类,和主类。电梯完全可以复用,只要设置上升下降速度,载客量,可停靠楼层,开关门时间就可以将多部电梯一样操作。
多部电梯和任务的关系是竞争关系,电梯去竞争任务而不是靠调度器调度任务,对调度器加锁,每次只有一部电梯可以进行调度,其他电梯就得等待。
对于载客量的限制,只要调度器每次判定电梯的总任务数量是否超过载客量(包括电梯内的和电梯外的),只要超过就不派发。
对于楼层问题,直达楼层就如作业二一样,并没有任何变化。而对于不直达楼层,我们需要对其进行分割,将其分成两个可以直达的任务,即分割第一任务和分割第二任务,这就需要解决以下几个任务:1.何时分割。我在Input类就进行了分割操作,一输入并不马上加入任务队列,而是先进行分割操作。2.如何保证先做完分割出的第一任务,再执行第二任务(即先送到中转楼层,再去接他到目的楼层,不然会造成去接但是人还没到的情况),因此我在调度器中加入了waitqueue和wastequeue,waitqueue存储的就是分割出来的第二任务,而wastequeue是电梯inelelist中被remove的元素。每次调用调度器,就检索整个wastequeue,查找其中是否有和waitqueue中相同的id(因为中转的是同一个人),有就把waitqueue中的请求加入主请求队列等待电梯竞争,没有就找下一个,并且把wastequeue的元素都清空。这样就可以保证时间的先后性,但是多次的循环检索可能会造成计算时间过长。
同步控制
新增的waitqueue和wastequeue都为多线程共享,因此我将其都调用线程安全类,使用ArrayBlockingQueue和ConcurrentHashMap两个类,保证其线程安全,同时在使用调度器的代码块加上锁,从而保证每次只有一部电梯调用调度器。这样就完成了整个同步控制流程。
BUG的出现
这次最严重的bug就是时间问题,CPU常常超出所规定的时间,原本我采用第二次作业的版式,只在电梯类的最后加入一条sleep()指令来完成暂时的休眠,然而第三次作业的调度器类太过于臃肿,其中循环检索太多使CPU运行时间过长,所以我就在每次调用调度器就notifyall并wait,并且在调度器内部也穿插了sleep,然而这就会导致另一个问题,当另外两个电梯结束线程后,就会使最后一个电梯永远停留在wait状态,于是我就使用wait(time),当wait超过time的时间就自动唤醒,从而解决了一个电梯运行的无限等待问题。
查找BUG策略
对于自身BUG查找,使用print法,在每次线程调度的开头和结尾都使用print,并且使用部分模拟数据来测试。
对于查找他人BUG,并没有很好的办法。
三次作业总结
这三次作业,让我对于多线程编程有了更加深刻的认识。它和单线程有着很大的差别,对我们的编程能力有着很大的考验,这就需要我们严谨的架构能力和对线程安全的重视。 我的架构能力确实有所提升,第二三次作业都是使用同一个架构。然而我对同步控制仍然还有很大缺陷和漏洞,我仅仅使用简单的线程安全类和一把锁,锁的范围过大也导致了某部分代码块需要运行过久时间,对notify和wait的使用也仍然生疏,这还需要多加练习。
BUAAOO第二单元总结之电梯问题的更多相关文章
- OO第二单元——多线程(电梯)
OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...
- BUAAOO第二单元多线程电梯作业总结
第二单元多线程作业需要保证线程安全
- 2019年北航OO第二单元(多线程电梯任务)总结
一.三次作业总结 1. 说在前面 对于这次的这三次电梯作业,我采用了和几乎所有人都不同的架构:将每个人当作一个线程.这样做有一定的好处:它使得整个问题的建模更加自然,并且在后期人员调度变得复杂时,可以 ...
- OO第二单元作业——魔鬼电梯
简介 本单元作业分为三次 第一次作业:第一次作业要实现单部简单电梯,停靠所有楼层,无载客容量,性能分考量电梯运行时间. 第二次作业: 第二次作业实现多部电梯,电梯数量由初始化设定,每部电梯都停靠所有楼 ...
- BUAA-OO第二单元小结
一.设计策略 三次作业中,由于前两次作业都只有一部电梯,因此我的线程只有两个,一个等待队列输入进程,以及一个电梯运行进程.等待队列输入进程实现十分简单,只需要根据输入把request添加到等待队列即可 ...
- BUAAOO第二单元代码分析
第一次作业 设计思路与感想 第一次作业是要求有捎带的电梯实现, 第一次作业是花费的时间比较长的一次,花费了很多的时间去思考架构的问题.起初是想要搞三个线程的:输入线程,调度器线程和电梯线程,想要搞一个 ...
- oo第二单元——多线程魔鬼电梯
在初步认识了面向对象思想后,立刻进入了多线程的学习,本单元的难点主要是锁的理解,需要保证线程安全的同时防止死锁的发生,也要尽可能缩小锁的范围,提高性能.这一单元以电梯为载体,让我们从生活出发,从电梯运 ...
- OO第二单元总结——多线程电梯
第五次作业分析 1.设计策略 调度器采用单例模式,内部设请求队列,对请求队列的一切操作(查.增.删)都在调度器内完成,且都要求串行,从而确保线程安全.接收器和电梯是两个线程:接收器接受请求调用调度器来 ...
- 第二单元电梯调度作业 By Wazaki
figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...
随机推荐
- ProtoType原型和__Proto__原型链的详解
var arr = new Array(12,13,14,15,16,17,18); var arr2 = new Array(12,13); arr.sun=function(){ //用原型加Ar ...
- 20165306 Exp3 免杀原理与实践
Exp3 免杀原理与实践 一.实践内容概述 1.正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,加壳工具,使用shellcode编程 2.通过组合应用各种技 ...
- Uva12174 Shuffle(滑动窗口)
$play[i]$表示以$i$这个点结束的连续$s$个播放记录是否是无重复的,这样最后只需要枚举可能的播放时间,然后检查对应的播放区间是否是单独的就可以了.特殊情况是,出现的所有播放记录无重复,且长度 ...
- js if判断示例
){ ){ console.log("%0 pass") }else{ $(,v,function() { fla=; }); } }){ ){ console.log(" ...
- EM公式推导
纯手写,字很丑,人也很丑.. E步公式是怎么来的呢?推导步骤如下, EM算法核心思想是先给定初始θ,求样本X,和隐变量z的期望(实际上是个函数),可以画一个曲线,M步:然后不断滑动θ,找到使得期望最大 ...
- es6中promise ALL Race Resolve Reject finish的实现
function mypromise(func){ this.statue = "pending"; this.data = null; this.resolveCallback ...
- Fragment调用startActivityForResult导致的回调Activity无法获取正确的requestId的问题
今天遇到了一个问题 从Fragment内调用了startActivityForResult方法设置了requestId是1 但是在Activity内的onActivityResult的回调内 获得的r ...
- selenium在scrapy中的使用、UA池、IP池的构建
selenium在scrapy中的使用流程 重写爬虫文件的构造方法__init__,在该方法中使用selenium实例化一个浏览器对象(因为浏览器对象只需要被实例化一次). 重写爬虫文件的closed ...
- css选择器以及使用场景
1.选择器以及使用场景 id选择器:#header{} 类选择器:.header{} 元素选择器:div{} 子选择器:ul > li{} 后代选择器:div p{} 伪类选择器:a:hover ...
- FreeMaker入门介绍
一.FreeMaker介绍 FreeMarker是一款免费的Java模板引擎,是一种基于模板和数据生成文本(HMLT.电子邮件.配置文件.源代码等)的工具,它不是面向最终用户的,而是一款程序员使用的组 ...