2019年北航OO第二次博客总结
一、多线程电梯系列作业设计策略
1. 第一次作业——"FAFS傻瓜电梯"
第一次作业是先来先服务的"傻瓜电梯",我当时觉得这个设计未免太简单了,于是就在傻瓜电梯的基础上加上贪心算法,每次都执行电梯内外距离最近的请求(但是没有行进中的捎带)。由于第一次没有限制CPU时间,而且我的wait--notify用得不太熟,因此就采取了暴力轮询的方式。我将我将调度器线程写在了main函数中(这是个很不好的设计,第三次作业中将其改进)作为一个线程,电梯作为一个线程,输入作为一个线程一共三个线程。另外我设计了一个personlist的类以及一个person类,这个类里面有一个属性是flag,flag表示这个人的状态,我规定了三种状态(0:未上电梯1:在电梯内2:在电梯外),这样我就不需要对personlist做remove操作,只需要每次都遍历personlist看它里面所有person对象的状态即可,人上下电梯也就变成了对flag的更改。其中输入线程阻塞式输入,每次输入之后转换为一个person对象加入到personlist中,然后电梯线程不断的轮询:判断是否开门—>开关门—>寻找下一个最近的楼层—>去最近的楼层。在开关门阶段电梯只负责开关门,人上下电梯由调度器负责,电梯开关门是将开关门标志位置为true,然后电梯sleep,这时调度器线程控制人的上下,当输入线程结束后,调度器线程就会不断判断是否personlist中的所有人的flag均为2,如果是而且电梯线程的门是否已关标志位置true,则调度器线程结束,电梯线程我设置为守护线程,在调度器线程和输入线程都结束后它也会平稳结束。
2. 第二次作业——"ALS可捎带电梯"
第二次作业是同向请求可捎带的“ALS电梯”。我的线程设置和上次相同,有两点改变。第一,为了限制CPU时间避免暴力轮询,我使用了wait--notify让线程在不需运行的时候wait,这是就需要多线程的协同与通信。在输入线程每输入进来一次请求时,都会notifyAll一次,这时会唤醒电梯线程(我是采用标志位置true+notifyAll来实现唤醒特定线程的),在输入线程结束后会唤醒调度器线程看是否需要结束程序。在电梯线程中,每次开关门时要唤醒调度器线程让它负责人员上下,每次关门要唤醒调度器看程序是否可以结束。第二,在电梯运行过程中每到一层都会判断是否有同向可稍带请求,如果有的话就执行捎带,如果没有的话继续运行即可。
3. 第三次作业——"多电梯"
多电梯是一个很复杂的问题,我的思想就是化繁为简,ABC电梯被我设置成了三部ALS电梯,三个线程。每个电梯有一个调度器,也是三个线程。这样一共六个线程,电梯的三个线程为守护线程。只要一个电梯能够执行的请求我就会让一个电梯来执行,遇到了一个电梯执行不了的请求,我会设置一个固定的楼层让它换乘,这些工作在电梯读入请求前就做好了,这样每个电梯只管自己可以接的请求,就相当于三个ALS的结合,感觉也是我对"请求调度与请求实现分离"的理解。
二、基于度量对程序结构的分析
1. 第一次作业
1.1 复杂度分析
本文采用MetricsReloaded插件进行复杂度分析,其中OCavg代表类的方法的平均循环复杂度,WMC代表类的方法的总循环复杂度。
1.2 UML 类图分析
本文采用IDEA内置diagram生成类图。
优缺点分析:我新建了一个person类去存请求(就相当于人),personlist是请求的一个队列,有三个线程。优点是实现简单,把电梯选请求都放到电梯线程中易于编码;缺点是该分离的功能没有分离实现,调度请求本应该是调度器的功能却交给了电梯去实现。
1.3 时序图分析
1.4 SOLID法则分析
由于没有继承和接口,因此对于LSP,ISP,DIP法则暂不考虑。
对于SRP,每个类都有一个明确的职责。这个完成的不太好,我的电梯类既进行了运行电梯,又进行了选择请求(相当于调度),应该让每个类的功能明确且单一。
对于OCP,感觉实现的还可以,之后的两次作业都可以套用这个架构,电梯的运行机制基本不变,需要改变的只是调度的机制和扩充为三部电梯,符合“无需修改已有实现,通过扩充来添加新功能”。
2. 第二次作业
2.1 复杂度分析
2.2 UML 类图分析
优缺点分析:优点是实现简单,把电梯选请求都放到电梯线程中易于编码,还有一点就是我把所有的输出写成了一个Output类,体现了面向对象封装的思想;缺点是该分离的功能没有分离实现,调度请求本应该是调度器的功能却交给了电梯去实现。
2.3 时序图分析
2.4 SOLID法则分析
由于没有继承和接口,因此对于LSP,ISP,DIP法则暂不考虑。
对于SRP,每个类都有一个明确的职责。这个完成的不太好,我的电梯类既进行了运行电梯,又进行了选择请求(相当于调度),应该让每个类的功能明确且单一。
对于OCP,感觉实现的还可以,第三次作业就相当于三部这样的电梯,电梯的运行机制基本不变,ALS调度也不须改变,符合“无需修改已有实现,通过扩充来添加新功能”。
3. 第三次作业
3.1 复杂度分析
3.2 UML 类图分析
优缺点分析:优点是实现简单,把电梯选请求都放到电梯线程中易于编码;把所有的输出写成了一个Output类,体现了面向对象封装的思想;请求调度与请求执行分离,每个调度器和电梯相当于一个第二次作业,三个这样的系统就组成了这次作业,各类之间耦合度小,正确度高不宜出错。缺点是该分离的功能没有分离实现,调度请求本应该是调度器的功能却交给了电梯去实现;三个电梯和三个调度器本可以致谢两个类而我却在想保证正确性和性能想法的驱动下写了6个类。
3.3 时序图分析
3.4 SOLID法则分析
由于没有继承和接口,因此对于LSP,ISP,DIP法则暂不考虑。
对于SRP,每个类都有一个明确的职责。这个完成的还可以,虽然电梯类既进行了运行电梯,又进行了选择请求(相当于调度),但是每个电梯和对应的调度器的这样的小系统是功能非常单一的,系统之间耦合度低。
对于OCP,完成的不好,调度方法基本没有什么扩展能力,电梯越多复杂度越高,可扩展性低。
三、自动化测试与ReentrantLock
1. 自动化测试
由于我这三次作业的hack次数和被hack次数均为0,因此就来谈谈测试吧。多线程程序由于它程序执行结果的不确定性以及程序bug的难以复现性,再使用手动输入测试的方法显然不合时宜。这时我们就需要利用一些自动化的测试方式来测试我们的程序。主要的步骤是
1)使用python开启一个子进程,启动待测程序
2)通过sleep完成定时输入,和第一步骤中开启的子进程通信
3)检测输出是否符合要求
2. Lock与ReentrantLock
这三次作业处于对正确性的追求,我一直在用synchronize加锁,而在实现唤醒特定线程时我使用的方法是,设置特定线程标志为true然后notifyAll.然而,实际上完全可以用功能更为强大的Lock和实现了Lock接口的可重入锁ReentrantLock.Lock比synchronize的优势主要体现在三个方面:
1)可以让等待的锁响应中断
2)可以知道是否成功获得锁
3)可以提高多个线程进行读操作的效率
ReentrantLock+Condition可以更加优美的实现唤醒特定的线程。
四、心得与体会
1. 线程安全
为了提高程序的执行效率,我们常常采取多个线程并发执行的方式。然而多线程中线程执行时序的不确定性会导致线程不安全的事情存在。常见的read-then-write,check-then-modify模式的代码都会造成线程的不安全。那么在多线程编程中我们如何可以尽可能地保证线程安全呢?有以下几点值得注意的地方:
1)使用不可变对象,用final强制限制对属性成员的修改
2)保证操作的原子性:使用Atomic***类型变量
3)保证更改的及时可见:violate关键字的使用
4)读写访问的互斥控制:对需要同步的代码块加锁
总结起来,要想保证你的设计是线程安全的,这里有三个要素:
1)严格控制对象的发布与共享
2)将共享对象设计为线程安全类
3)线程类要保持简介
2. 设计原则
这三次作业我用到了面向对象的一些设计模式,还有我自己的一些设计原则
1)如果全局只存在一个对象(比如调度器),则采用单例模式构造调度器对象
2)如果一个对象的状态发生改变,需要另外一些依赖它的对象收到通知并自动更新,则可以使用观察者模式
3)Worker Thread模式也是一种很好的设计模式,应用于我们本次电梯作业可将请求调度与请求实现分离
4)实现简单,架构清晰,正确度要比性能分更重要
3. 一些反思与展望
时光飞逝,转眼间已经过去6次OO作业了,我们本学期的OO征程也已经过半,这两个月以来,虽然非常的辛苦,但是的的确确收获了很多,虽然自己还有很多不足,但是对本学期OO的前半部分还是比较满意的,希望自己不断努力,继续加油!
2019年北航OO第二次博客总结的更多相关文章
- OO第二次博客作业——电梯调度
OO第二次博客作业——电梯调度 前言 最近三周,OO课程进入多线程学习阶段,主要通过三次电梯调度作业来学习.从单部电梯的傻瓜式调度到有性能要求的调度到多部电梯的调度,难度逐渐提升,对同学们的要求逐渐变 ...
- OO第二次博客作业—17373247
OO第二次博客作业 零.写在前面 OO第二单元宣告结束,在这个单元里自己算是真正对面向对象编程产生了比较深刻的理解,也认识到了一个合理的架构为编程带来的极大的便利. (挂三次评测分数 看出得分接近等差 ...
- OO第二次博客作业(第二单元总结)
在我开始写这次博客作业的时候,窗外响起了希望之花,由此联想到乘坐自己写的电梯FROM-3-TO--1下楼洗澡,然后······ 开个玩笑,这么辣鸡的电梯肯定不会投入实际使用的,何况只是一次作业.还是从 ...
- 渐入OO课的深处,探索多线程的秘密——OO第二次博客总结
一次又一次的挑战,一次又一次全新的知识,我来到了多线程的面前 第五次作业 1.度量分析 >第五次作业由于很大程度上调用的是前两次电梯的一些代码,所以存在的问题与前几次也十分相似.同时由于第一次使 ...
- 2019年北航OO第二单元(多线程电梯任务)总结
一.三次作业总结 1. 说在前面 对于这次的这三次电梯作业,我采用了和几乎所有人都不同的架构:将每个人当作一个线程.这样做有一定的好处:它使得整个问题的建模更加自然,并且在后期人员调度变得复杂时,可以 ...
- oo第二次博客作业
多线程协同与同步控制总结 第五次作业-多线程电梯 本次作业是我第一次接触多线程,建立了请求模拟器.调度器和电梯运行三种线程.请求模拟器负责在输入后识别有效请求:调度器在扫描有效请求后将新的请求加入请求 ...
- OO第二次博客作业--第二单元总结
第一次作业 1. 设计策略 第一次作业,一共三个线程,主线程.输入线程和电梯线程,有一个共享对象--调度器(队列). 调度的策略大多集中到了电梯里,调度器反而只剩下一个队列. 2. 基于度量的分析 类 ...
- OO第二次博客
过去三周里,我们完成了多线程电梯的程序设计与构造.这是我第一次接触多线程编程.我感觉最大的困难在于多个线程中的操作,谁先谁后,不是像以前写的单线程程序那样严格确定,所以心里常常会比较慌.尤其是因为多线 ...
- OO第二单元博客
三次作业的设计策略 第一次作业 多线程协同控制 第一次作业只需要两个线程和一个公共缓冲区: 负责读取输入并把它添加进命令队列的线程,即生产者 负责从命令队列中取出命令执行的线程,即消费者 再加上一个缓 ...
随机推荐
- ubuntu的命令行状态和图形化界面切换 (转)
1.ubuntu命令状态切换到图形化界面 startx 如果命令不能识别 执行 sudo apt-get install xinit 还需要提前安装桌面环境 比如 gnome 比如 KDE 安装方法 ...
- ASP.Net Web API 输出缓存(转)
出处:http://www.cnblogs.com/ajilisiwei/p/6112078.html 原文的转载地址:http://www.strathweb.com/2012/05/output- ...
- XP+Android手机DIY家庭视频点播系统-历时3周全力打造吊丝的幸福生活
需求场景(纯熟虚构): 1. 哥电脑里有200G电影copy到手机上看没那么大空间,copy一部看一部删除一部,很是不方便也费时间. 2. 小林同学需求比较旺盛但是媳妇总有不方便的时候,家里有 ...
- [GO]go context的deadline方法
package main import ( "time" "context" "fmt" ) func main() { d := time ...
- Nginx搭建后,图片存储在Tomcat上,前端无法回显图片问题
一.Nginx与Tomcat连接搭建的环境,Nginx设置了前端的访问路径为 (1)前端代码配置: root /usr/local/nginx/html; index index.html index ...
- Linux守护进程编写方法及原理
什么守护进程? 守护进程是运行在后台的一种用来提供服务的进程,他脱离控制台独立运行,守护进程是一种很有用的进 程. Linux的大多数服务器就是用守护进程实现的.比如,Internet服务器inetd ...
- C/C++编程可用的Linux自带工具
GNU Binary Utilities或binutils是一整套的编程语言工具程序,用来处理许多格式的目标文件.当前的版本原本由在Cygnus Solutions的程序员以Binary File D ...
- LVS初步
LVS初步 一见 目录 目录 1 1. 前言 2 2. 思考 2 3. 名词解释 2 4. OSI参考模型 3 5. LVS架构 4 5.1. 负载均衡器(Load Balancer) 4 5.2. ...
- IntricCondition和expliciteCondition比较
IntricCondition 和 expliciteCondition 的区别 与 intrinsicLoc和expliciteLock的区别很相似, expliciteCondition提供了更多 ...
- 试题 C: 数列求值 蓝桥杯
试题 C: 数列求值本题总分: 10 分[问题描述]给定数列 1, 1, 1, 3, 5, 9, 17, …,从第 4 项开始,每项都是前 3 项的和.求第 20190324 项的最后 4 位数字.[ ...