三次作业的设计策略

第一次作业

多线程协同控制

第一次作业只需要两个线程和一个公共缓冲区:

  • 负责读取输入并把它添加进命令队列的线程,即生产者
  • 负责从命令队列中取出命令执行的线程,即消费者
  • 再加上一个缓冲区:命令队列

相对应的线程之间的互斥和同步操作及对应的处理方法

互斥操作:

  • 互斥1:对命令队列的读-写操作和写-写操作之间的互斥:

    • 我采用的方法是使用Java的ReadWriteLock来实现互斥操作,通过封装Vector类为自己的线程安全的命令队列类,实现读-写互斥和写-写互斥。
  • 互斥2:为了实现当生产者线程结束后,消费者线程再结束,必须使用一个公共指示变量,对于这个指示变量,同样需要相应的读写互斥和写写互斥
    • 同样使用ReadWriteLock实现互斥操作

同步操作:

  • 同步1:命令队列空时,消费者线程需要等到生产者线程添加了命令后才可以执行

    • 对于线程之间的同步操作,因为互斥操作已经使用了Lock实现没有使用synchronize关键字,所以我并没有使用waitnotify方法,而是采用更加灵活的Java的Semaphore信号量类,因为信号量并不需要在synchronize语句块内使用。
  • 同步2:同时还有当生产者线程即输入线程结束后,消费者线程才可以结束
    • 这里使用公共的指示变量来实现,检测到指示变量被设置并且此时命令队列为空,那么电梯调度线程结束

分析程序结构

由于每一次作业都是在前一次作业的修改上进行,所以对uml图的分析放在第三次作业那一块,这里简要分析一下第一次作业的类及其作用。

对于第一次作业,我写了五个类

  • Constant,专门用于存储一些多个类都可能用的上的全局常量,比如电梯的最低楼层,最高楼层,这样写对之后程序的修改也方便
  • Main,main类
  • RequestQueue类,自己使用VectorReadWriteLock实现的线程安全的命令队列类,可以实现读-写互斥和写-写互斥
  • Lift类,电梯类,实现电梯的基本运行方法,包括开关门和上下楼
  • Handler类,调度器类,实现对电梯的调度策略

SOLID原则检查

  • 单一责任原则遵守的自认为不错,每个类都只负起自己该负的责任
  • 开放封闭原则遵守的自认为不错,代码更新无需删除原来的代码。
  • 里氏替换原则:没有使用继承
  • 接口隔离原则:没有定义接口,也没有必要定义接口
  • 依赖倒置原则:未使用抽象类

分析bug

公测和互测均未出现bug

第二次作业

多线程协同控制

第二次作业相比第一次作业,在多线程的设计上并没有什么太大的变化

调度策略设计

只是在调度策略上进行了改进,所以这里简要阐述我的调度策略。

作业指导书上推荐使用的是捎带算法,但是自己在实现捎带算法时感觉逻辑很复杂(也许是我想的太复杂),故权衡之下决定使用扫描算法,即电梯通过不断的从需要到达的最低层一直到需要到达的最高层,再从最高层到最底层,按照这样的次序依次扫描直到输入线程结束。

类结构分析

这次作业我在前一次作业的基础上增加了一个类:Request类,即是对输入接口中的PersonRequest的封装,因为对于扫描算法来说,在到达某个指令的目标楼层时,需要检查当前指令是否已经走过初始楼层,所以需要记录指令的state

metric分析

可以看出Main类中,因为放入一些无关的逻辑,导致复杂性增加

bug分析

在公测和互测中都没有发现bug

但是在写作业的过程中,犯了一个致命的错误,就是竟然使用了轮询,而且我一开始还没有意识到使用了轮询,于是在公测中出现了CPU_TIME_EXCEED的错误。当时我对于wait和notify的使用还不清楚,对于线程间的协同控制也没有理解太到位才导致了这个错误,修改后我对wait和notify的作用理解更深了。

第三次作业

第三次作业相比于第二次作业,大的不同点有两个

  • 首先有三部电梯,这样在调度策略上就需要改进,不仅考虑单个电梯如何调度自己的请求,更要考虑请求如何更好的分配到三部电梯
  • 一些请求靠一部电梯无法执行,需要两部电梯协同执行,这一点比较难

多线程协同控制

这一次作业因为需要两部电梯协同执行,所以我选择把一些不能用一部电梯完成的命令拆分,这样我在输入处理和三部电梯的调度器之间又增加了一个总调度器,如图

这样做逻辑更加清晰,但是因为增加了一个公共缓冲区,所以需要的同步互斥操作更多了,不过使用之前的同步互斥方法就可以解决它。

调度策略

这次有三部电梯,所以调度算法的设计有很大的空间,但是我因为时间关系,没法设计出更好的调度算法,所以简单采用了平均分配的方法,即对于一部电梯就可以执行的指令,如果三部电梯都可以执行,那么就按照电梯1,电梯2,电梯3,电梯1...的顺序分配。

拆分策略

第三次作业涉及到了两部电梯的协同,我一开始想到了两种方法:

  • 利用图寻路算法,这样无论它怎么改变每个电梯的可停楼层,都可以通过寻路算法得到最优的协作办法
  • 硬编码,利用多层if else把情况分类

这里我选择了第二种,第二种编写起来逻辑简单,不过扩展性差。

类结构分析

这次类相对于上次作业,增加了MainHandler类,即控制位于输入线程和三个电梯线程之间的公共缓冲区,以及分配命令给三个电梯线程

下面是这次的UML图

度量结果

这里只给出有红色字体的分析图,可以看出main函数的嵌套深度大,代码复杂性高,究其原因是我把拆分命令的逻辑放在了这里,正确的设计应该把它单独写到MainHandler类中

SOLID原则

  • 单一责任原则:每个类都只负起自己该负的责任
  • 开放封闭原则:代码更新无需删除原来的代码。
  • 里氏替换原则:没有使用继承
  • 接口隔离原则:没有定义接口,也没有必要定义接口
  • 依赖倒置原则:未使用抽象类

bug修复

在这次作业中,一开始的公测没有测出我的一个bug,这个bug可能导致ArrayIndexOutOfBounds这个异常,因为我把每个电梯的可用楼层用数组的形式存着,但并没有做上限检查,导致出现这个bug,目前已经修复

心得体会

线程安全

我认为线程之间的协同,总的来说就是要考虑两个方面:

  • 同步控制,即线程的执行顺序需要控制,在命令队列为空时,消费者线程 是不能从中取命令的,必须要等待生产者线程放入命令后消费者线程才可以继续执行。
  • 互斥控制,即读-写互斥和写-写互斥。

对于这两个方面:

  • 同步控制我使用了Semaphore
  • 互斥控制我使用了ReadWriteLock

在线程安全方法,只要分析出需要的同步和互斥这两方面的操作,并分别针对性的进行控制,线程安全就没有太大问题

设计模式

  • 我看了HeadFirst设计模式这本书,越看越觉得设计模式是很灵活的,就和倚天屠龙记里的太极拳法一样,把该忘的都忘了,只记住一些必要的原则,就可以在设计时找到最适合的那个模式。
  • 好的设计模式可以在重构程序时事半功倍

OO第二单元博客的更多相关文章

  1. oo第二单元博客总结

    P1 设计结构 三次作业的架构都没有较大的改动,基本上都是靠调度器接受输入的请求并放入队列,然后调度器根据不同的电梯的当前状态来把请求分配至不同电梯的请求队列中,最后电梯再根据自己的请求队列去运行.因 ...

  2. OO第二次博客作业—17373247

    OO第二次博客作业 零.写在前面 OO第二单元宣告结束,在这个单元里自己算是真正对面向对象编程产生了比较深刻的理解,也认识到了一个合理的架构为编程带来的极大的便利. (挂三次评测分数 看出得分接近等差 ...

  3. OO第二次博客作业——电梯调度

    OO第二次博客作业——电梯调度 前言 最近三周,OO课程进入多线程学习阶段,主要通过三次电梯调度作业来学习.从单部电梯的傻瓜式调度到有性能要求的调度到多部电梯的调度,难度逐渐提升,对同学们的要求逐渐变 ...

  4. OO第二次博客作业(第二单元总结)

    在我开始写这次博客作业的时候,窗外响起了希望之花,由此联想到乘坐自己写的电梯FROM-3-TO--1下楼洗澡,然后······ 开个玩笑,这么辣鸡的电梯肯定不会投入实际使用的,何况只是一次作业.还是从 ...

  5. OO第二次博客作业--第二单元总结

    第一次作业 1. 设计策略 第一次作业,一共三个线程,主线程.输入线程和电梯线程,有一个共享对象--调度器(队列). 调度的策略大多集中到了电梯里,调度器反而只剩下一个队列. 2. 基于度量的分析 类 ...

  6. 北航OO(2020)第二单元博客作业

    第二单元第一次作业 多线程设计策略 第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程.输入线程获取请求并发送给调度器线程:调度器线程通过查询电梯线程的状态(等待.停靠以及移动),并综合 ...

  7. 渐入OO课的深处,探索多线程的秘密——OO第二次博客总结

    一次又一次的挑战,一次又一次全新的知识,我来到了多线程的面前 第五次作业 1.度量分析 >第五次作业由于很大程度上调用的是前两次电梯的一些代码,所以存在的问题与前几次也十分相似.同时由于第一次使 ...

  8. 2019年北航OO第二次博客总结

    一.多线程电梯系列作业设计策略 1. 第一次作业——"FAFS傻瓜电梯" 第一次作业是先来先服务的"傻瓜电梯",我当时觉得这个设计未免太简单了,于是就在傻瓜电梯 ...

  9. oo第二次博客-三次电梯调度的总结与反思

    本单元从电梯调度相关问题层层深入,带领我们学习并运用了了多线程相关的知识. 三次电梯调度依次为单电梯单容量.单电梯可携带.多电梯可携带. 一.我的设计 在第一次作业中,使用了最简单的FIFO调度方法. ...

随机推荐

  1. JMeter配置JDBC测试SQL Server/MySQL

    一.配置SQL Server 1.下载sql驱动,将sqljdbc4.jar放到JMeter安装目录/lib下. 2.启动JMeter,右键添加->配置文件->JDBC Connectio ...

  2. Charles对移动APP抓包(https)

    1.下载安装Charles 2.设置代理 (1)查看默认端口:Proxy->Proxy Settings  在这个页面会看到HTTP Proxy的默认端口是8888 (2)查看当前电脑的IP:H ...

  3. Palindromes in a Tree CodeForces - 914E

    https://vjudge.net/problem/CodeForces-914E 点分就没一道不卡常的? 卡常记录: 1.把不知道为什么设的(unordered_map)s换成了(int[])s ...

  4. UVa 11437 (梅涅劳斯定理) Triangle Fun

    题意: 给出三角形ABC顶点的坐标,DEF分别是三边的三等分点.求交点所形成的三角形PQR的面积. 分析: 根据梅涅劳斯定理,我们得到: ,解得 另外还有:,解得 所以AR : RP : PD = 3 ...

  5. mongoDB内置文档定义

    在最近的设计数据库时,犯了一个低级的错误,就是设置内置文档是定义了错误了,导致数据取不出,去找了很多资料都无法解决.最后看了一了一下自己设置的model文件.配置错误,所以导致数据取不出了. 数据库时 ...

  6. 外文翻译 《How we decide》被情感愚弄 第二节

    本节阅读感言:我们在遭受损失后,很容易破罐子破摔,做出更糟糕的决定. 书的导言 本章第一节 情感系统的缺陷会产生很重要的影响.想一想股票市场,一个典型的随机系统的例子.短期的波动无法给未来长期的股市情 ...

  7. 初学web前端,掌握这些就足够了!

    Web开发如今是如日中天,热的发烫.那我们应该怎么学习呢?这不光是初学者,很多学了几年的人也会有些迷茫或者彷徨,大家也都知道不断学习是不可避免的,不学习肯定要掉队:那怎么学效率更高,那些是坑,那些是路 ...

  8. JAVA解析XML的几种方法

    DOM DOM Document Object Model 文档对象模型.在应用程序中,基于DOM的解析器将一个XML文档转换成一个对象模型的集合(DOM树),应用程序正是通过对这个对象模型的操作,来 ...

  9. VINS-Fusion代码阅读(四)

    pts_i和pts_j:具体指什么含义?(分别为第l个路标点在第i, j个相机归一化相机坐标系中的观察到的坐标,P¯¯¯cil \bar{P}^{c_i}_l Pˉ lc i​ ​ 和 P¯¯¯cjl ...

  10. navicat 链接数据库查看的工具 可以同时查看各种数据库 MySql SqlServer

    navicat 链接数据库查看的工具 Navicat_Premium_10.0.11.0_XiaZaiBa