OO第二单元电梯作业总结
目录
目录一、第一次作业分析设计策略基于度量分析程序结构二、第二次作业分析设计策略基于度量分析程序结构三、第三次作业分析设计策略基于度量分析程序结构四、分析自己程序的bug五、发现别人程序bug所采用的策略六、心得体会线程安全设计原则
一、第一次作业分析
设计策略
采用生产者-消费者模式,调度器作为托盘,请求处理器不断向托盘提交请求,电梯不断从托盘中获取请求执行,当托盘为空时,进入等待。其中调度器是共享对象,请求处理器和电梯是两个线程。
线程安全方面,由于理论知识不扎实,由请求处理器和电梯分别管理了自己的线程安全问题。
基于度量分析程序结构
1. 代码规模
2. 经典OO度量
- 方法复杂度
ev(G) | iv(G) | v(G) | |
---|---|---|---|
Total | 24 | 30 | 32 |
从中可看出,两个线程的run()
方法复杂度都较高,分析其原因是因为在run()
方法中对线程安全进行了管理。
- 类复杂度
WMC:类方法加权衡量(类中方法数)
OCavg:类平均循环复杂度
各类方法数差距不大。电梯方法数最多是因为将电梯运行拆分成了各个模块(开门、上下人、关门)的行为。
3. 类图
- 优点:运用了生产者、消费者模式,将调度器作为托盘的模式将请求输入模拟器与电梯的耦合度降至最低
- 缺点:两个线程分别考虑自己的线程安全问题,存在“多管闲事”的行为,应交由调度器统一管理,线程只负责自己的行为
4. UML时序图
5. SOILD设计原则
- SRP:符合
- OCP:未涉及增添新功能
- LSP:符合
- ISP:未涉及接口使用
- DIP:未涉及
二、第二次作业分析
设计策略
基本上仍然沿用第一次作业中生产者-消费者的模式,请求进入请求处理器后被送入调度器,调度器再进行处理后分配。本次在ALS的基础上增加了自己的优化,即将电梯第一目标是主请求优化为在运行过程中变更目的楼层,思路是接送可接送的最大请求数,有些类似于LOOK算法。
调度器取到请求时先存放入缓存区,电梯索要捎带队列时,调度器根据电梯状态,检索缓存区和请求仓库,分配合适的请求,并将缓存区的请求flush入请求仓库。请求仓库相当于等待队列,缓存区相当于刚来的等待队列,分配的请求相当于即将进入电梯的队列。
电梯无等待队列,分为待进入和待走出两个队列,取到的捎带队列放入待进入队列,只要待进入队列中有请求处于电梯当前所在楼层,就将其送入电梯。电梯线程仅需根据待进入和待走出两个队列设定自己的方向、目标楼层,不停运输请求即可。
基于度量分析程序结构
1. 代码规模
2. 经典OO度量
- 方法复杂度
ev(G) | iv(G) | v(G) | |
---|---|---|---|
Total | 47 | 78 | 91 |
从整体可看出,整个设计的圈复杂度(v(G))最大,模块设计复杂度(iv(G))次之,基本复杂度(e(G))最小,说明代码的结构化程度整体较好,但模块中条件语句泛滥,模块间调用关系也较繁多。
分析单个模块,QueueController.requestTakeable()
的复杂度最“突出”,是因为在这个函数中传入了电梯的当前状态(所处楼层、方向、目标楼层),用于判断捎带队列是否可捎带。当时在完成这一函数时,由于是为实现功能而不断添添补补,导致模块逻辑混乱,毫无设计可言。
对于Elevator
类中的getFinalFloor()
,checkPersonInandOut()
等方法中,也是由于过度看重功能的实现,而忽略了对模块高内聚、低耦合的设计目的进行检查。
- 类复杂度
电梯中的方法权重过大,虽是按照功能划分了不同的方法模块,但由于考虑不周,产生了一些没有必要的模块划分。也可能是由于职能分配不周,让电梯多做了些事情。
3. 类图
- 优点:保证捎带正确的基础上,改进的电梯调度比原生ALS调度算法快了些。
- 缺点:设计与实现的不统一。在实现的过程中由于设计的某些偏差,又重新更改设计,进行增添修改,造成逻辑混乱、调试困难。即使保证了正确性,却很难再进行扩展。
4. UML时序图
5. SOILD设计原则
- SRP:不符合。调度这一职能在调度器和电梯中被分割执行,调度器先根据状态分配捎带队列,电梯再根据自己的队列和捎带队列确定状态,二者都在完成调度这一功能;且电梯中入
personInandOut()
等方法也涉嫌“多管闲事”(负责开关门、取捎带队列等) - OCP:不符合。改变调度策略的同时,电梯中的运行方法也进行了大幅度修改。
- LSP:符合
- ISP:未涉及接口使用
- DIP:未涉及
三、第三次作业分析
设计策略
采用了Worker-Thread模式,但有一点不同,需求request没有execute()
方法,而是交由工人(电梯)来完成需求。本次设计还采用了观察者模式中的一些思路,即电梯可以加入/退出调度器,支持电梯数量的动态变化,且调度器中有新需求(更新)时,会通知观察者(电梯)们。
请求输入处理器与前两次作业基本相同,作出的改进是由调度器管理线程安全。
调度器中存放三部电梯的可达楼层表,以及与三部电梯相对应的三个请求队列。本次还将请求抽象为乘客的成员,乘客还有自己的属性:直达或是换乘。调度器通过判断乘客请求,将其标记为直达或换乘,若为直达,分配到对应可直达电梯;若为换乘,分配到可到达其出发楼层的电梯。电梯的乘客分配优先级为A>B>C,此次没有想出能同时保证线程安全和性能的优先级变更策略。
电梯中有两个队列:等待队列和在电梯中的队列。通过两个队列确定电梯的运行方向、目标楼层,调度完全在电梯中进行。换乘采用的策略是,若电梯内部乘客是换乘的乘客,则在其他两部电梯的可达楼层中找“港口”(即电梯当前楼层是某一电梯的可达楼层),若当前楼层是“港口”则把乘客送出电梯,向调度器送入新请求,请求出发楼层是当前楼层,目标楼层是乘客目标楼层,此时总有一部电梯能让乘客直达目标楼层。这种策略简化了电梯换乘应考虑的线程安全问题,但不易进行优化。
三部电梯获取乘客的线程安全问题由调度器解决,调度器中三部电梯对于的请求队列若有更新,则通知对应的电梯。除此之外电梯独立运行,互不干扰。
基于度量分析程序结构
1. 代码规模
2. 经典OO度量
- 方法复杂度
ev(G) | iv(G) | v(G) | |
---|---|---|---|
Total | 76.0 | 121.0 | 137.0 |
对于基本复杂度ev(G),有两个方法出现“赤字”,分别是Elevator.checkTransfer()
(ev(G)=4)和Scheduler.putRequest()
(ev(G)=5)。对于Elevator.checkTransfer()
,由于使用了存放在调度器中的电梯可达楼层表floorMap
,用于判断乘客是否可在当前楼层换乘,故结构化程度有所降低。Scheduler.putRequest()
中使用Passenger
类创建了乘客,并两次遍历电梯可达楼层,第一次找直达电梯,若没有,第二次找换乘电梯。模块略显臃肿,结构化程度低。
对于模块设计复杂度iv(G),Elevator.getAimFloorThroughList()
(iv(G)=9)出现了“赤字”。这一方法功能为通过电梯的两条队列,寻找最终的目标楼层(即寻找可接送的最高楼层)。在方法中调用了大量类的get方法获取private属性(Passenger.getKind()
,PersonRequest.getTo/FromFloor()
等),以及队列的isEmpty()
,size()
等方法,还调用了电梯自己的子方法寻找楼层,模块间耦合程度过高。
对于圈复杂度v(G),各个大的功能模块数值都较大,其中判断结构较多,但没有“赤字”出现。
- 类复杂度
电梯中的方法仍然过于臃肿,原因是因为调度完全交由电梯管理,电梯中包含有调度、改变自身状态、询问乘客上/下电梯等功能的方法
3. 类图
- 优点:使用了Vector,CopyOnWriteArrayList等线程安全的容器,各个模块分工明确:请求处理器接受请求传入调度器,调度器负责请求的分配、电梯启动的控制,电梯负责运输乘客
- 缺点:电梯模块过于臃肿,有“神仙类”与“傻瓜类”并存的嫌疑。结构分层没有仔细考虑,仍使用不带包结构、类能省则省的设计策略进行设计。
4. UML时序图
5. SOILD设计原则
- SRP:基本符合,类和方法的功能在设计时进行了区分,但内部可能有些细节在实现时添入而破坏了原则
- OCP:不符合,抛弃了前两次作业,进行了重构
- LSP:符合
- ISP:未涉及接口使用
- DIP:未涉及,若电梯种类不同则需考虑
四、分析自己程序的bug
本次电梯系列作业,三次作业的强测与互测中都没有被发现bug。
在第一次作业中,课下寻找自己程序的bug采用的是IDEA自带的多线程debug。
赋予线程名字,在想要查看的线程中设置断点进行单步调试,分析线程的行为。但一次只能进入一个线程进行调试,对于多个线程的状态,使用Jprofiler进行查看。
第二次作业中仍采用上述方法,主要是对调度器分配捎带队列的行为和电梯自身调度行为进行检查,所以比较有效,但由于设计不好,导致de出一个bug长出两个的窘况。此外,在第二次互测期间还实现了定点投放的python脚本(在“发现别人bug采用的策略“一栏中给出源码)。
第三次作业由于设计花费时间较多,所以实现时逻辑较清晰,采用的是”先解决线程安全问题,再实现功能“的策略,同一时间只解决一个类型的问题。调试方法采用的也是第一第二次作业中的方法。
五、发现别人程序bug所采用的策略
三次作业的互测环节0战绩,问题在于不知道往哪个方向攻破别人的代码,线程安全与调度问题似乎处理得都挺好。此外,多线程bug难以复现也是问题之一。
本单元测试策略与第一单元的差异在于,测试的重心由输入输出边界转变为了线程安全与调度,更多的考虑整体设计、功能性能,而不是钻牛角尖,卡边角。
第一次作业较为简单,着重点在于检查线程安全问题,但没有收获。
第二、第三次作业使用了自己的定点投放脚本,将自己课下积累的数据逐一投放,统计互测屋中所有人的电梯运行时间,若平均运行时间较长,从代码中寻找漏洞;若平均运行时间较短,参考他的代码并汲取精华。
下面放出定点投放的脚本(多组同时投放的数据会出现bug):
#!/bin/bash
# coding=utf-8
import subprocess
import time
import linecache
import re
import signal
def showInFile(infilename, sourcename):
#"warout/"是每个人的输出结果
outfilename = "warout/" + sourcename + ".txt"
with open(outfilename, "w") as outfile:
#开启子进程,通过命令行打开jar包
cmd = "java -jar src/" + sourcename + "/out/" + sourcename + ".jar"
print("------------------------------------------")
print(cmd, end='\n\n')
res = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
stdout=outfile.fileno(), stderr=subprocess.PIPE)
#投放数据
infile = open(infilename, "r")
start = time.time()
castData(infile, start, res)
res.communicate()
#取最后一行输出
finalout = open(outfilename, "r")
print("last line : " + finalout.readlines()[-1])
print('end\n')
def castData(infile, starttime, res):
for line in infile:
instr = line
if (line == ""):
print(res.poll())
if (line == '\n'):
continue
# get input time
intime = re.findall(r"\[(.+?)\]", instr)
intime = float(intime[0])
# get request
instr = instr.replace('[' + str(intime) + ']', "")
# sleep until specific time
span = time.time() - starttime
shouldsleep = intime - span
if (shouldsleep < 0 and intime != 0):
print("error input!")
continue
else:
time.sleep(abs(shouldsleep))
# debug note
print("at " + str(time.time() - starttime))
# print(line)
print(line, end='')
# send data into stdin
res.stdin.write(bytes(instr, 'utf-8'))
res.stdin.flush()
print("flush all\n")
if __name__ == '__main__':
showInFile("input.txt", "saber")
showInFile("input.txt", "rider")
showInFile("input.txt", "lancer")
showInFile("input.txt", "berserker")
showInFile("input.txt", "assassin")
#showInFile("input.txt", "archer")
showInFile("input.txt", "caster")
showInFile("input.txt", "alterego")
showInFile("input.txt", "kk") #本人
print("all done")
六、心得体会
线程安全
在线程安全方面,本单元作业算是一个入门。只停留在synchronized锁对象的层面上,对于一些高阶的线程安全处理方法还不了解。
第一次和第二次作业仅需处理请求处理器和电梯间的线程安全问题,做法是在线程内部独自处理。请求处理器接受到请求时,通过共享对象——调度器进行notify,电梯不断运行,处理完当前请求队列后获取调度器中的请求,若没有则通过调度器wait,释放锁。第二次作业还需考虑获取捎带队列的情况,解决办法为电梯进行一次服务时,只有第一次取请求队列时判断是否等待,其余时刻取捎带队列时,即使捎带队列为空也不会等待。
第三次作业中涉及了四个线程:一个请求处理器和三部电梯,虽然看似使线程安全问题复杂了,但仔细想,只要能够在调度器中完成请求的分配,在电梯取请求时控制好线程安全,电梯运行时完全独立,就解决了问题。
还有一方面需要思考的,是换乘问题,最初考虑使用Passenger
类存放该乘客的换乘步骤,但对于换乘请求的分解没有想到好的对策,既能解决自动设置步骤,又能保证可扩展性,且分解请求的做法还需考虑请求执行的先后顺序,又是一大线程安全问题。在查看互测屋代码与同袍交流的过程中,发现有同学使用枚举的办法进行换乘请求分配,或许也是一个不错的选择,只是如果有新的电梯加入时,需要人工进行换乘情况的添加与修改。个人对换乘问题,采用的是寻找港口的办法,如果有必须换乘的乘客,电梯直观在出发楼层将他接入,在运行过程中找到能够让他乘坐其他电梯直达目的楼层的“港口”,就将其请求处理后送回调度器,这样就无需考虑步骤的先后问题,电梯还是在独立运行,换乘的乘客请求交由调度器处理,绕开了三部电梯间的线程安全问题。
总结本单元作业,基本的线程安全问题能够使用synchronized,notify,wait等常见处理方法解决,但对于其他线程安全控制办法并没有太多了解,还需在后续阅读书籍、博客以及练习中提升。
设计原则
本单元作业,体会最深的是,将编程这项工作从算法层上升到了设计层,更强调整体的框架设计,而不是停留在单个模块的性能优化。从此才体会到,为什么有人说“编程是门艺术”。好的架构设计,让人看一眼就觉得充满美感,而杂乱无章的设计,则使人心情烦躁。
前两次作业由于没有了解到“SOLID”设计原则,对整个框架的设计还停留在为实现功能而设计的层面上。设计模式采用的是生产者-消费者模式,生产消费关系比较清晰,但内部模块设计只为完成功能,没有考虑设计原则。
第三次作业前的那一堂课,了解到了原来面向对象程序设计还有这样那样的设计原则,再反思自己从前写过的代码,仿佛是一堆废品。第三次作业在Worker-Thread设计模式的基础上,考虑了整个架构的模块职能分配、可扩展性等等。最终完成的设计可允许添加更多电梯,也可以进行电梯请求分配优先级调整、电梯内部调度调整等优化,但本人没有想好即保证平均运行时间提升又不破坏框架的优化方法,所以仍沿用第二次作业的调度策略,稍作修改。
本单元作业还有些遗憾,就是没有使用多态、继承、接口等面向对象的特性,里氏原则、ISP原则也无法得到运用,作业显得没那么“面向对象”了。希望在后续单元的练习中,能够考虑得更仔细周到,运用多态、继承与接口最大化实现代码复用,使得设计结构抽象更加合理。此外,还需通过学习Java设计模式,在不同场景中灵活运用,解决不同的实际问题,不断提升自己的面向对象程序设计能力。
html { overflow-x: initial !important }
:root { --bg-color: #ffffff; --text-color: #333333; --select-text-bg-color: #B5D6FC; --select-text-font-color: auto; --monospace: "Lucida Console",Consolas,"Courier",monospace }
html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased }
body { margin: 0; padding: 0; height: auto; bottom: 0; top: 0; left: 0; right: 0; font-size: 1rem; line-height: 1.42857; overflow-x: hidden; tab-size: 4 }
iframe { margin: auto }
a.url { word-break: break-all }
a:active, a:hover { outline: 0 }
.in-text-selection, ::selection { text-shadow: none; background: var(--select-text-bg-color); color: var(--select-text-font-color) }
#write { margin: 0 auto; height: auto; width: inherit; word-break: normal; word-wrap: break-word; position: relative; white-space: normal; overflow-x: visible; padding-top: 40px }
#write.first-line-indent p { text-indent: 2em }
#write.first-line-indent li p, #write.first-line-indent p * { text-indent: 0 }
#write.first-line-indent li { margin-left: 2em }
.for-image #write { padding-left: 8px; padding-right: 8px }
body.typora-export { padding-left: 30px; padding-right: 30px }
.typora-export .footnote-line, .typora-export li, .typora-export p { white-space: pre-wrap }
@media screen and (max-width: 500px) { body.typora-export { padding-left: 0; padding-right: 0 } #write { padding-left: 20px; padding-right: 20px } .CodeMirror-sizer { margin-left: 0 !important } .CodeMirror-gutters { display: none !important } }
#write li>figure:first-child { margin-top: -20px }
#write ol, #write ul { position: relative }
img { max-width: 100%; vertical-align: middle }
button, input, select, textarea { color: inherit; font: inherit inherit inherit inherit inherit / inherit inherit }
input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0 }
*, ::after, ::before { box-sizing: border-box }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p, #write pre { width: inherit }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p { position: relative }
h1, h2, h3, h4, h5, h6 { break-after: avoid-page; break-inside: avoid; orphans: 2 }
p { orphans: 4 }
h1 { font-size: 2rem }
h2 { font-size: 1.8rem }
h3 { font-size: 1.6rem }
h4 { font-size: 1.4rem }
h5 { font-size: 1.2rem }
h6 { font-size: 1rem }
.md-math-block, .md-rawblock, h1, h2, h3, h4, h5, h6, p { margin-top: 1rem; margin-bottom: 1rem }
.hidden { display: none }
.md-blockmeta { color: rgba(204, 204, 204, 1); font-weight: 700; font-style: italic }
a { cursor: pointer }
sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.7); color: rgba(85, 85, 85, 1); border-radius: 4px; cursor: pointer }
sup.md-footnote a, sup.md-footnote a:hover { color: inherit; text-transform: inherit }
#write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit }
figure { overflow-x: auto; margin: 1.2em 0; max-width: calc(100% + 16px); padding: 0 }
figure>table { margin: 0 !important }
tr { break-inside: avoid; break-after: auto }
thead { display: table-header-group }
table { border-collapse: collapse; border-spacing: 0; width: 100%; overflow: auto; break-inside: auto; text-align: left }
table.md-table td { min-width: 32px }
.CodeMirror-gutters { border-right: 0; background-color: inherit }
.CodeMirror-linenumber { user-select: none }
.CodeMirror { text-align: left }
.CodeMirror-placeholder { opacity: 0.3 }
.CodeMirror pre { padding: 0 4px }
.CodeMirror-lines { padding: 0 }
div.hr:focus { cursor: none }
#write pre { white-space: pre-wrap }
#write.fences-no-line-wrapping pre { white-space: pre }
#write pre.ty-contain-cm { white-space: normal }
.CodeMirror-gutters { margin-right: 4px }
.md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; position: relative !important }
.md-diagram-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0; padding-bottom: 8px; overflow-x: auto }
#write .md-fences.mock-cm { white-space: pre-wrap }
.md-fences.md-fences-with-lineno { padding-left: 0 }
#write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre; overflow-x: auto }
.md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px }
.CodeMirror-line, twitterwidget { break-inside: avoid }
.footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em }
.footnotes+.footnotes { margin-top: 0 }
.md-reset { margin: 0; padding: 0; border: 0; outline: 0; vertical-align: top; background: left top; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; -webkit-tap-highlight-color: transparent; line-height: normal; font-weight: 400; text-align: left; box-sizing: content-box; direction: ltr }
li div { padding-top: 0 }
blockquote { margin: 1rem 0 }
li .mathjax-block, li p { margin: 0.5rem 0 }
li { margin: 0; position: relative }
blockquote>:last-child { margin-bottom: 0 }
blockquote>:first-child, li>:first-child { margin-top: 0 }
.footnotes-area { color: rgba(136, 136, 136, 1); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal }
#write .footnote-line { white-space: pre-wrap }
@media print { body, html { border: 1px solid rgba(0, 0, 0, 0); height: 99%; break-after: avoid; break-before: avoid } #write { margin-top: 0; padding-top: 0; border-color: rgba(0, 0, 0, 0) !important } .typora-export * { -webkit-print-color-adjust: exact } html.blink-to-pdf { font-size: 13px } .typora-export #write { padding-left: 32px; padding-right: 32px; padding-bottom: 0; break-after: avoid } .typora-export #write::after { height: 0 } @page { margin-top: 20mm margin-right: 0 margin-bottom: 20mm margin-left: 0 } }
.footnote-line { margin-top: 0.714em; font-size: 0.7em }
a img, img a { cursor: pointer }
pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background: rgba(204, 204, 204, 1); display: block; overflow-x: hidden }
p>.md-image:only-child:not(.md-img-error) img, p>img:only-child { display: block; margin: auto }
p>.md-image:only-child { display: inline-block; width: 100% }
#write .MathJax_Display { margin: 0.8em 0 0 }
.md-math-block { width: 100% }
.md-math-block:not(:empty)::after { display: none }
[contenteditable="true"]:active, [contenteditable="true"]:focus { outline: 0; box-shadow: none }
.md-task-list-item { position: relative; list-style-type: none }
.task-list-item.md-task-list-item { padding-left: 0 }
.md-task-list-item>input { position: absolute; top: 0; left: 0; margin-left: -1.2em; margin-top: calc(1em - 10px); border: none }
.math { font-size: 1rem }
.md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-radius: 10px }
.md-toc-content { position: relative; margin-left: 0 }
.md-toc-content::after, .md-toc::after { display: none }
.md-toc-item { display: block; color: rgba(65, 131, 196, 1) }
.md-toc-item a { text-decoration: none }
.md-toc-inner:hover { text-decoration: underline }
.md-toc-inner { display: inline-block; cursor: pointer }
.md-toc-h1 .md-toc-inner { margin-left: 0; font-weight: 700 }
.md-toc-h2 .md-toc-inner { margin-left: 2em }
.md-toc-h3 .md-toc-inner { margin-left: 4em }
.md-toc-h4 .md-toc-inner { margin-left: 6em }
.md-toc-h5 .md-toc-inner { margin-left: 8em }
.md-toc-h6 .md-toc-inner { margin-left: 10em }
@media screen and (max-width: 48em) { .md-toc-h3 .md-toc-inner { margin-left: 3.5em } .md-toc-h4 .md-toc-inner { margin-left: 5em } .md-toc-h5 .md-toc-inner { margin-left: 6.5em } .md-toc-h6 .md-toc-inner { margin-left: 8em } }
a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit }
.footnote-line a:not(.reversefootnote) { color: inherit }
.md-attr { display: none }
.md-fn-count::after { content: "." }
code, pre, samp, tt { font-family: var(--monospace) }
kbd { margin: 0 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgba(36, 39, 41, 1); background: rgba(255, 255, 255, 1); border: 1px solid rgba(173, 179, 185, 1); border-radius: 3px; box-shadow: 0 1px rgba(12, 13, 14, 0.2), inset 0 0 2px rgba(255, 255, 255, 1); white-space: nowrap; vertical-align: middle }
.md-comment { color: rgba(162, 127, 3, 1); opacity: 0.8; font-family: var(--monospace) }
code { text-align: left; vertical-align: initial }
a.md-print-anchor { white-space: pre !important; border-style: none !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0 !important; outline: 0 !important; background: left top !important; text-shadow: initial !important }
.md-inline-math .MathJax_SVG .noError { display: none !important }
.html-for-mac .inline-math-svg .MathJax_SVG { vertical-align: 0.2px }
.md-math-block .MathJax_SVG_Display { text-align: center; margin: 0; position: relative; text-indent: 0; max-width: none; max-height: none; min-height: 0; min-width: 100%; width: auto; overflow-y: hidden; display: block !important }
.MathJax_SVG_Display, .md-inline-math .MathJax_SVG_Display { width: auto; display: inline-block !important }
.MathJax_SVG .MJX-monospace { font-family: var(--monospace) }
.MathJax_SVG .MJX-sans-serif { font-family: sans-serif }
.MathJax_SVG { display: inline; font-style: normal; font-weight: 400; line-height: normal; zoom: 90%; text-indent: 0; text-align: left; text-transform: none; letter-spacing: normal; word-spacing: normal; word-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0; min-height: 0; border: 0; padding: 0; margin: 0 }
.MathJax_SVG * { }
.MathJax_SVG_Display svg { vertical-align: middle !important; margin-bottom: 0 !important }
.os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif }
.md-diagram-panel>svg { max-width: 100% }
[lang="mermaid"] svg, [lang="flow"] svg { max-width: 100% }
[lang="mermaid"] .node text { font-size: 1rem }
table tr th { border-bottom: 0 }
video { max-width: 100%; display: block; margin: 0 auto }
iframe { max-width: 100%; width: 100%; border: none }
.highlight td, .highlight tr { border: 0 }
.CodeMirror { height: auto }
.CodeMirror.cm-s-inner { }
.CodeMirror-scroll { overflow-y: hidden; overflow-x: auto; z-index: 3 }
.CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler { background-color: rgba(255, 255, 255, 1) }
.CodeMirror-gutters { border-right: 1px solid rgba(221, 221, 221, 1); white-space: nowrap }
.CodeMirror-linenumber { padding: 0 3px 0 5px; text-align: right; color: rgba(153, 153, 153, 1) }
.cm-s-inner .cm-keyword { color: rgba(119, 0, 136, 1) }
.cm-s-inner .cm-atom, .cm-s-inner.cm-atom { color: rgba(34, 17, 153, 1) }
.cm-s-inner .cm-number { color: rgba(17, 102, 68, 1) }
.cm-s-inner .cm-def { color: rgba(0, 0, 255, 1) }
.cm-s-inner .cm-variable { color: rgba(0, 0, 0, 1) }
.cm-s-inner .cm-variable-2 { color: rgba(0, 85, 170, 1) }
.cm-s-inner .cm-variable-3 { color: rgba(0, 136, 85, 1) }
.cm-s-inner .cm-string { color: rgba(170, 17, 17, 1) }
.cm-s-inner .cm-property { color: rgba(0, 0, 0, 1) }
.cm-s-inner .cm-operator { color: rgba(152, 26, 26, 1) }
.cm-s-inner .cm-comment, .cm-s-inner.cm-comment { color: rgba(170, 85, 0, 1) }
.cm-s-inner .cm-string-2 { color: rgba(255, 85, 0, 1) }
.cm-s-inner .cm-meta { color: rgba(85, 85, 85, 1) }
.cm-s-inner .cm-qualifier { color: rgba(85, 85, 85, 1) }
.cm-s-inner .cm-builtin { color: rgba(51, 0, 170, 1) }
.cm-s-inner .cm-bracket { color: rgba(153, 153, 119, 1) }
.cm-s-inner .cm-tag { color: rgba(17, 119, 0, 1) }
.cm-s-inner .cm-attribute { color: rgba(0, 0, 204, 1) }
.cm-s-inner .cm-header, .cm-s-inner.cm-header { color: rgba(0, 0, 255, 1) }
.cm-s-inner .cm-quote, .cm-s-inner.cm-quote { color: rgba(0, 153, 0, 1) }
.cm-s-inner .cm-hr, .cm-s-inner.cm-hr { color: rgba(153, 153, 153, 1) }
.cm-s-inner .cm-link, .cm-s-inner.cm-link { color: rgba(0, 0, 204, 1) }
.cm-negative { color: rgba(221, 68, 68, 1) }
.cm-positive { color: rgba(34, 153, 34, 1) }
.cm-header, .cm-strong { font-weight: 700 }
.cm-del { text-decoration: line-through }
.cm-em { font-style: italic }
.cm-link { text-decoration: underline }
.cm-error { color: rgba(255, 0, 0, 1) }
.cm-invalidchar { color: rgba(255, 0, 0, 1) }
.cm-constant { color: rgba(38, 139, 210, 1) }
.cm-defined { color: rgba(181, 137, 0, 1) }
div.CodeMirror span.CodeMirror-matchingbracket { color: rgba(0, 255, 0, 1) }
div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgba(255, 34, 34, 1) }
.cm-s-inner .CodeMirror-activeline-background { }
.CodeMirror { position: relative; overflow: hidden }
.CodeMirror-scroll { height: 100%; outline: 0; position: relative; box-sizing: content-box }
.CodeMirror-sizer { position: relative }
.CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar { position: absolute; z-index: 6; display: none }
.CodeMirror-vscrollbar { right: 0; top: 0; overflow: hidden }
.CodeMirror-hscrollbar { bottom: 0; left: 0; overflow: hidden }
.CodeMirror-scrollbar-filler { right: 0; bottom: 0 }
.CodeMirror-gutter-filler { left: 0; bottom: 0 }
.CodeMirror-gutters { position: absolute; left: 0; top: 0; padding-bottom: 30px; z-index: 3 }
.CodeMirror-gutter { white-space: normal; height: 100%; box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block }
.CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: left top !important; border: none !important }
.CodeMirror-gutter-background { position: absolute; top: 0; bottom: 0; z-index: 4 }
.CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4 }
.CodeMirror-lines { cursor: text }
.CodeMirror pre { border-radius: 0; border-width: 0; background: left top; font-family: inherit; font-size: inherit; margin: 0; white-space: pre; word-wrap: normal; color: inherit; z-index: 2; position: relative; overflow: visible }
.CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal }
.CodeMirror-code pre { border-right: 30px solid rgba(0, 0, 0, 0); width: fit-content }
.CodeMirror-wrap .CodeMirror-code pre { border-right: none; width: auto }
.CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0 }
.CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto }
.CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden }
.CodeMirror-measure { position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden }
.CodeMirror-measure pre { position: static }
.CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right: none; width: 0 }
.CodeMirror div.CodeMirror-cursor { visibility: hidden }
.CodeMirror-focused div.CodeMirror-cursor { visibility: inherit }
.cm-searching { background: rgba(255, 255, 0, 0.4) }
@media print { .CodeMirror div.CodeMirror-cursor { visibility: hidden } }
:root { --side-bar-bg-color: #fafafa; --control-text-color: #777 }
html { font-size: 16px }
body { font-family: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; color: rgba(51, 51, 51, 1); line-height: 1.6 }
#write { max-width: 860px; margin: 0 auto; padding: 30px 30px 100px }
#write>ul:first-child, #write>ol:first-child { margin-top: 30px }
a { color: rgba(65, 131, 196, 1) }
h1, h2, h3, h4, h5, h6 { position: relative; margin-top: 1rem; margin-bottom: 1rem; font-weight: bold; line-height: 1.4; cursor: text }
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { text-decoration: none }
h1 tt, h1 code { font-size: inherit }
h2 tt, h2 code { font-size: inherit }
h3 tt, h3 code { font-size: inherit }
h4 tt, h4 code { font-size: inherit }
h5 tt, h5 code { font-size: inherit }
h6 tt, h6 code { font-size: inherit }
h1 { padding-bottom: 0.3em; font-size: 2.25em; line-height: 1.2; border-bottom: 1px solid rgba(238, 238, 238, 1) }
h2 { padding-bottom: 0.3em; font-size: 1.75em; line-height: 1.225; border-bottom: 1px solid rgba(238, 238, 238, 1) }
h3 { font-size: 1.5em; line-height: 1.43 }
h4 { font-size: 1.25em }
h5 { font-size: 1em }
h6 { font-size: 1em; color: rgba(119, 119, 119, 1) }
p, blockquote, ul, ol, dl, table { margin: 0.8em 0 }
li>ol, li>ul { margin: 0 }
hr { height: 2px; padding: 0; margin: 16px 0; background-color: rgba(231, 231, 231, 1); border: 0 none; overflow: hidden; box-sizing: content-box }
li p.first { display: inline-block }
ul, ol { padding-left: 30px }
ul:first-child, ol:first-child { margin-top: 0 }
ul:last-child, ol:last-child { margin-bottom: 0 }
blockquote { border-left: 4px solid rgba(223, 226, 229, 1); padding: 0 15px; color: rgba(119, 119, 119, 1) }
blockquote blockquote { padding-right: 0 }
table { padding: 0; word-break: initial }
table tr { border-top: 1px solid rgba(223, 226, 229, 1); margin: 0; padding: 0 }
table tr:nth-child(2n), thead { background-color: rgba(248, 248, 248, 1) }
table tr th { font-weight: bold; border-top: 1px solid rgba(223, 226, 229, 1); border-right: 1px solid rgba(223, 226, 229, 1); border-bottom: 0; border-left: 1px solid rgba(223, 226, 229, 1); text-align: left; margin: 0; padding: 6px 13px }
table tr td { border: 1px solid rgba(223, 226, 229, 1); text-align: left; margin: 0; padding: 6px 13px }
table tr th:first-child, table tr td:first-child { margin-top: 0 }
table tr th:last-child, table tr td:last-child { margin-bottom: 0 }
.CodeMirror-lines { padding-left: 4px }
.code-tooltip { box-shadow: 0 1px 1px rgba(0, 28, 36, 0.3); border-top: 1px solid rgba(238, 242, 242, 1) }
.md-fences, code, tt { border: 1px solid rgba(231, 234, 237, 1); background-color: rgba(248, 248, 248, 1); border-radius: 3px; padding: 2px 4px 0; font-size: 0.9em }
code { background-color: rgba(243, 244, 244, 1); padding: 0 2px }
.md-fences { margin-bottom: 15px; margin-top: 15px; padding-top: 8px; padding-bottom: 6px }
.md-task-list-item>input { margin-left: -1.3em }
@media print { html { font-size: 13px } table, pre { break-inside: avoid } pre { word-wrap: break-word } }
.md-fences { background-color: rgba(248, 248, 248, 1) }
#write pre.md-meta-block { padding: 1rem; font-size: 85%; line-height: 1.45; background-color: rgba(247, 247, 247, 1); border: 0; border-radius: 3px; color: rgba(119, 119, 119, 1); margin-top: 0 !important }
.mathjax-block>.code-tooltip { bottom: 0.375rem }
.md-mathjax-midline { background: rgba(250, 250, 250, 1) }
#write>h3.md-focus::before { left: -1.5625rem; top: 0.375rem }
#write>h4.md-focus::before { left: -1.5625rem; top: 0.285714rem }
#write>h5.md-focus::before { left: -1.5625rem; top: 0.285714rem }
#write>h6.md-focus::before { left: -1.5625rem; top: 0.285714rem }
.md-image>.md-meta { border-radius: 3px; padding: 2px 0 0 4px; font-size: 0.9em; color: inherit }
.md-tag { color: rgba(167, 167, 167, 1); opacity: 1 }
.md-toc { margin-top: 20px; padding-bottom: 20px }
.sidebar-tabs { border-bottom: none }
#typora-quick-open { border: 1px solid rgba(221, 221, 221, 1); background-color: rgba(248, 248, 248, 1) }
#typora-quick-open-item { background-color: rgba(250, 250, 250, 1); border-top: 1px solid rgba(254, 254, 254, 1); border-right: 1px solid rgba(229, 229, 229, 1); border-bottom: 1px solid rgba(229, 229, 229, 1); border-left: 1px solid rgba(238, 238, 238, 1) }
.on-focus-mode blockquote { border-left-color: rgba(85, 85, 85, 0.12) }
header, .context-menu, .megamenu-content, footer { font-family: "Segoe UI", Arial, sans-serif }
.file-node-content:hover .file-node-icon, .file-node-content:hover .file-node-open-state { visibility: visible }
.mac-seamless-mode #typora-sidebar { background-color: var(--side-bar-bg-color) }
.md-lang { color: rgba(180, 101, 77, 1) }
.html-for-mac .context-menu { --item-hover-bg-color: #E6F0FE }
#md-notification .btn { border: 0 }
.dropdown-menu .divider { border-color: rgba(229, 229, 229, 1) }
img { zoom: 80% }
.typora-export li, .typora-export p, .typora-export, .footnote-line { white-space: normal }
OO第二单元电梯作业总结的更多相关文章
- OO第二单元——电梯作业总结
前言 本单元作业主要以设计电梯来实现多线程编程.本章主要学习了如何使用多线程以及如何确保多线程安全,从电梯的调度策略中学会了如何简单地使用synchronized锁来控制线程安全. 首先,明确锁的两个 ...
- OO第二单元电梯线程系列总结作业
电梯系列第一次作业 功能描述: 傻瓜电梯无需考虑超载捎带 线程模式: Producer-Consumer Pattern 思路: 第一次作业是一个傻瓜电梯,分别有一个生产者生成电梯指令(也就是Inpu ...
- OO第二单元(电梯)单元总结
OO第一单元(求导)单元总结 这是我们OO课程的第二个单元,这个单元的主要目的是让我们熟悉理解和掌握多线程的思想和方法.这个单元以电梯为主题,从一开始的最简单的单部傻瓜调度(FAFS)电梯到最后的多部 ...
- 北航OO第二单元——电梯调度
三次作业要求简介 特点:目的选层电梯 在电梯的每层入口,都有一个输入装置,让每个乘客输入自己的目的楼层.电梯基于这样的一个目的地选择系统进行调度,将乘客运送到指定的目标楼层. 第一次: 在任意时刻输入 ...
- 电梯也能无为而治——oo第二单元作业总结
oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...
- oo第二单元作业总结
oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...
- 【OO学习】OO第二单元作业总结
OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...
- OO第二单元多线程电梯总结
OO第二单元多线程电梯总结 第一次作业 设计思路 Input为输入线程,负责不断读取请求并将读到的请求放入调度器中. Dispatcher为调度器,是Input线程和Elevator线程的共享对象,采 ...
- OO第二单元——多线程(电梯)
OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...
随机推荐
- MongoDB(2)- 安装 MongoDB
MacOS 安装 MongoDB 博主目前都用 mac 电脑练习,所以这里重点讲 MacOS 安装方式 系统要求 MongoDB 4.4 社区版支持 macOS 10.13 或更高版本 安装 Home ...
- Vue获取Abp VNext Token
Abp VNext默认没公开访问Token的Api,但有个问题Cookie方式如果是手机或桌面程序不如Token方便 Axios默认是Json方式提交,abp登录需要使用application/x-w ...
- Selenium系列4-元素定位
前言 说起元素定位,一定是学习自动化测试绕不开的第一道关,无论是web端的UI自动化还是移动端的自动化,在需要首先对元素进行定位才可以完成对元素的操作已达成测试目的,在Selenium中,可以使用fi ...
- HTML基本概念及基本标签
HTML基本概念及基本语法 1.HTML的基本概念 1.1 B/S.C/S基本概念 B/S(Browser/Server):指的是浏览器端与服务器端工作模式,优点相对节省本地存储空间,不足是需要占用 ...
- linux停止nginx服务 未成功
在上线新功能的时候,需要将服务器停掉,防止在更新过程中有用户进行操作额外的数据. 1:查看nginx主进程: ps -ef | grep nginx 这里root 后面的数字表示:主进程号nginx后 ...
- 一个完整的SEO优化方案
一个完整的SEO优化方案主要由四个小组组成: 一.前端/页编人员 二.内容编辑人员 三.推广人员 四.数据分析人员 接下来,我们就对这四个小组分配工作. 首先,前端/页编人员主要负责站内优化,主要从四 ...
- Jmeter系列(23)- 常用逻辑控制器(2) | 事务控制器Transaction Controller
事务控制器(Transaction Controller) 作用 选择一些请求,作为事务,放在该控制器下 比如:我有三个请求,注册.登录.下单.这三个请求其实就是一个下单完成过程,可以作为一个下单事务 ...
- promise对象总结
一.Promise是异步编程的一种解决方案,它是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Promis ...
- web带宽估算方法
每个连接约占用10Kb的带宽,以3万总用户数和10%的在线率计算,并按照10%的冗余率,服务器总带宽=每秒总连接数*10Kbps /(1-冗余率)/1024. 带宽占用(Mbps)=30000*10% ...
- CF755G-PolandBall and Many Other Balls【倍增FFT】
正题 题目链接:https://www.luogu.com.cn/problem/CF755G 题目大意 \(n\)个东西排成一排,每个组可以选择一个单独的物品或者两个连续的物品,一个物品不同同时在两 ...