oo第二单元——多线程魔鬼电梯
在初步认识了面向对象思想后,立刻进入了多线程的学习,本单元的难点主要是锁的理解,需要保证线程安全的同时防止死锁的发生,也要尽可能缩小锁的范围,提高性能。这一单元以电梯为载体,让我们从生活出发,从电梯运行的角度理解多线程,同时学习和应用生产者-消费者模式来帮助我们编程。在一部可捎带电梯的基础上越来越贴近生活,进行了电梯数量的扩展,载客人数的限制
homework5
这次作业是单部可捎带电梯,主要是初步应用多线程编程,保证线程安全,防止死锁的发生。
UML
采用生产者-消费者模型,Main类创建其他类,启动线程;CubbyHole类是托盘,In类是生产者线程,Elevator类是消费者线程,In把乘客信息放到CubbyHole中,Elevator从CubbyHole取出乘客信息。
方法复杂度度量
总体来说方法复杂度较低,少数方法耦合度高。
自己程序的bug
在强测和互测中均未发现bug。在调试过程中出现电梯线程在应该结束的时候因为还没有执行到跳出循环那一步就满足了wait的条件而wait,导致的线程无法结束的问题。
关于互测
考虑到IDEA手动输入的时间不容易控制,所以用自动化测试,但是并不是很完善,只测试了乘客有没有送达,没有测试电梯逻辑的正确性。所以在互测中并没有发现别人的bug。
协作图
homework6
这次作业的电梯数量不确定,需要利用循环开启线程,保证每个线程都能被开启,并且都能获得资源。同时电梯有最大载客量,在调度的时候需要考虑。还加入了负数楼层,这时需要注意0层是不存在的,所以我在1到-1和-1到1的时候特判一下。
UML
在homework5的结构上进行功能扩展,仍然采用生产者-消费者模式。在CubbyHole上新增从生产者线程获得电梯数量的方法,为电梯类新增最大载客数和当前载客数的变量。在性能方面没有做太多的优化,只是避免了多个电梯抢一个乘客这种情况。
方法复杂度度量
总体方法的复杂度较低,电梯类的run方法的复杂度较高,可能还是有一定的面向过程的编程思维没有改过来,一些比较细节的东西还是放在了run这个顶层的方法中。
自己程序的bug
在强测中没有出现bug,在互测中出现了一个runtime_error的bug。考虑到不希望进程频繁被唤醒占用cpu资源,所以只在一些我认为需要唤醒的方法中加入唤醒操作,但是并没有考虑周全,在一些应该有唤醒的方法中没有唤醒操作,导致进程无法唤醒,也就无法结束。
关于互测
这次互测我采用了手动测试与自动测试相结合的方式。手动测试主要针对电梯最大载客量的限制,同时在同一层放入很多乘客看是否有超载的发生,还针对所有的电梯线程是否都启动并且都能获得资源进行测试,如果没有,可能会存在RTLE的发生。
协作图
homework7
这次作业可以新增电梯,也就是需要在接收到命令的时候再次开启一个线程,还设置了不同类型的电梯,不同的电梯最大载客量不同,可以停靠的层数也不同,所以在电梯类构造的时候就根据不同的类型将这些参数设置好。由于电梯在一些楼层不停靠,这就涉及到换乘问题,主要采用了打表的形式,舍弃换乘两次的情况,只会换乘一次。
UML
仍然采用经典的生产者-消费者模型。不足是存取数据和调度都放在“托盘”里执行,所以托盘类非常的复杂,耦合度较高,不利于扩展。
方法复杂度度量
总体方法复杂度不高,大体按照不同的功能写了不同的方法,但是可以看到电梯线程的run方法复杂度较高,这是不足的。run方法应该是最顶层的结构,应该注意层次化设计。
自己程序的bug
在强测和互测中均未发现bug。分析自己的程序,因为调度比较弱,所以可能在指令比较刁钻的时候面临超时的风险。在调试过程出现的错误有忘记换乘的需要而让电梯提前结束。
关于互测
同样,由于在IDEA手工输入对时间的控制较难,所以使用自动化测试,检查了有无丢失乘客,并且可以输出程序的最后一条输出的输出时间,检查RTLE类型的错误。
协作图
拓展性分析
功能设计
使用生产者-消费者模型,托盘类负责存取乘客和给电梯分配乘客。电梯类按照电梯运行的逻辑顺序,主要有获取主请求、下乘客、上乘客、移动的功能,电梯的很多参数,如处于的楼层,去往的楼层,电梯内乘客数和乘客信息等都是显性表示的,方法的功能也比较的独立,所以如果要新添加功能会比较方便。
性能设计
考虑到正确性,在性能方面没有做太多的优化,所以后期可以参考一些电梯调度算法,如look,sstf从单部电梯的角度来优化性能;也可以参考图的最短路径的一些算法,对换乘站的计算等方面来优化性能;也可以考虑如果有多部相同类型的电梯,让它们只在较小的区域活动等优化方法。
三次作业的设计策略
三次作业都采用生产者-消费者的模式,建立主类,生产者类,托盘类和消费者类,生产者线程和消费者线程只会获取托盘的数据和方法,做好托盘的保护,两个线程不会互相调用。在多个电梯的作业中,并没有为每个电梯设置等待队列,而是采用电梯到达一层时将总的等待队列的符合要求的乘客都接走这种方式。乘客的分配和电梯调度都由托盘类根据等待队列的乘客状态做出判断。这样做有利有弊,好处是只有一个共享资源,所以不会线程互相等待对方的资源而出现死锁的情况。坏处是这个托盘类非常的复杂,方法、变量都特别多,耦合度也很高,不符合类的要求,可扩展性也比较差。
在设计中满足SOLID设计原则
SRP原则
按照生产者、消费者、托盘划分类,功能明确,但是由于题目比较复杂,在迭代过程中因为并没有拓展更多的类,所以类非常复杂,这个需要反思与改进。
OCP原则
把电梯按照逻辑划分为寻找主类、下乘客、上乘客、移动这四个主要的方法,在迭代时原有功能没有太多改动。
LSP原则
本次作业并没有过多涉及。
DIP原则
本次作业并没有过多涉及。
心得体会
多线程编程与单线程编程的思维相差比较大,调试的难度,出错的概率也很大。初次接触多线程的时候,我觉得弄清楚“锁”这个概念很重要,公共资源就像是一个房间,每次只能一个线程拿到锁进入房间,没有拿到锁但是又要进入房间的就需要等待,执行完synchronized块后自动归还锁,wait是交出锁并且到等待区,并不是等待锁的队列,notify和notifyAll是从等待区唤醒线程进入等待队列。我觉得生产者-消费者模型对我们写多线程程序很有帮助,这次作业非常符合这个模型,所以应用这个模型可以让我非常快的上手这次的作业,并且对于三次作业即使功能上有了很多的扩展,但是这个模型都非常适用,所以相比于第一单元的疯狂重构,这个单元没有出现大的重构。
我觉得最难的部分是调试,单线程用起来非常香的断点调试在多线程也就不香了。我觉得想要调试多线程,首先是先要把整个运行的逻辑梳理一遍,什么条件电梯不应该运行而应该睡眠,什么条件线程应该结束,特别是唤醒操作,有些synchronized方法块中必须要有唤醒操作,不然可能会导致线程不能被唤醒从而无法结束,有些方法块中加入唤醒是不必要的,导致线程不应该唤醒的时候唤醒,占用资源。我主要使用printf来调试,比如遇到cpu时间特别长的情况,这时在while循环中设置自增的变量,每次循环就加1并输出,有很多次我的程序都出现了这个数值特别大,原因是条件设置不合理所以应该wait的情况下没有wait导致。
oo第二单元——多线程魔鬼电梯的更多相关文章
- OO第二单元——多线程(电梯)
OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...
- OO第二单元作业——魔鬼电梯
简介 本单元作业分为三次 第一次作业:第一次作业要实现单部简单电梯,停靠所有楼层,无载客容量,性能分考量电梯运行时间. 第二次作业: 第二次作业实现多部电梯,电梯数量由初始化设定,每部电梯都停靠所有楼 ...
- OO第二单元多线程电梯总结
OO第二单元多线程电梯总结 第一次作业 设计思路 Input为输入线程,负责不断读取请求并将读到的请求放入调度器中. Dispatcher为调度器,是Input线程和Elevator线程的共享对象,采 ...
- OO第二单元多线程电梯总结分析
一.概述 这一部分的作业考察的关注点与上一次的作业有所不同,上一次的考察重点主要集中在输入输出的判定以及多态的考察上面,而这一次是让我们进行多线程程序的调度与开发.这次开发过程中最大的感受就是自己之前 ...
- 电梯也能无为而治——oo第二单元作业总结
oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...
- BUAAOO第二单元多线程电梯作业总结
第二单元多线程作业需要保证线程安全
- oo第二单元作业总结
oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...
- 【OO学习】OO第二单元作业总结
OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...
- OO第二单元小结
OO第二单元小结 一.三次作业代码分析. 1.第一次作业 第一次作业是单部电梯的傻瓜调度,由于其过分傻瓜,所以第一次作业我只有两个类,一个main,一个电梯,main类负责不断从输入流中读取命令,如果 ...
随机推荐
- NGK 路演美国站,SPC空投与NGK项目安全
最近,NGK全球巡回路演在美国最大城市纽约市落下帷幕,本次路演有幸邀请了NGK方面代表迈尔逊,纽约当地区块链大咖维克多以及美国当地社群意见代表乔治等人. 路演一开始,美国当地路演师Viko首先致辞,V ...
- Asp.Net Core学习笔记:(二)视图、模型、持久化、文件、错误处理、日志
TagHelper 入门 优点:根据参数自动生成,不需要手写超链接,类似Django模板里面的url命令. 在ViewImport中添加TagHelper @addTagHelper *,Micros ...
- 那些容易犯错的c++保留字
本文首发 | 公众号:lunvey 目前正在学习vc++6.0开发,而这里面使用的是c++98标准. 保留字,也称关键字,是指在变量.函数.类中不得重新声明的名称. c++98中大致有48个保留字,这 ...
- Cassandra数据操作管理工具tableplus
一.概述 Cassandra是一个NoSQL数据库,具有类SQL CQL入口,基本语法与SQL保持一致.其实笔者认为 Cassandra的自带的cqlsh已经满足本的需求:如: 但是用习惯了数据库操作 ...
- JAVA学生宿舍管理系统
转: JAVA学生宿舍管理系统 需要的工具 1.SQL Server 2.Eclipse 3.JDBC连接数据库驱动 https://download.microsoft.com/download/A ...
- 《C++ Primer》笔记 第10章 泛型算法
迭代器令算法不依赖于容器,但算法依赖于元素类型的操作. 算法永远不会执行容器的操作.算法永远不会改变底层容器的大小. accumulate定义在头文件numeric中,接受三个参数,前两个指出需要求和 ...
- docker的安装和基本的docker命令、镜像和容器的操作
1.yum 包更新到最新 yum update 2.安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的 yum insta ...
- Java 运行时数据区和内存模型
运行时数据区是指对 JVM 运行过程中涉及到的内存根据功能.目的进行的划分,而内存模型可以理解为对内存进行存取操作的过程定义.总是有人望文生义的将前者描述为 "Java 内存模型" ...
- struts2.0中ognl栈的解析
ongl详解: ValueStack是Struts2的一个接口,字面意义为值栈,OgnlValueStack是 ValueStack的实现类,客 户端发起一个请求,struts2架构会创建一个acti ...
- 订单退款&重复支付需求疑问点归纳整理
更新历史记录: 更新内容 更新人 更新时间 新建 Young 2020.12.10 16:45 更新产品疑问解答 Young 2020.12.11 10:14 更新退款权益终止时间 Young 2 ...