0 写在前面

终于,我们结束了OO第二个周期的作业,这几次作业也就是我们所早有耳闻的“电梯”作业。

现在就来写关于这几次作业的总结,主要会分析自己的设计方案和总体收获。


1 多线程协同与同步控制(设计方案)

我的这三次作业都采用了多线程的方式来编码。主要是因为作为一名重修生(去年一些意外,放弃了OO),我早就知道电梯最后会演变成多电梯等。

其次就是,我也知道这次作业是为了锻炼我们的多线程编码能力、了解线程同步与安全设置的,所以我也从一开始就采用了多线程的设计。

我的程序主要分为几大部分,并且这几个部分也就是我的几个线程:

  • 输入处理部分:使用给定的接口,处理输入的数据并将处理后的请求放入请求缓冲池中。
  • 调度器部分:从请求缓冲池读取待处理请求,根据情况分给不同的电梯或者做其他处理后分给电梯(最后一次用到)。
  • 电梯部分:每个电梯有自己的待处理请求池,电梯根据自己请求池内的人和自己的运行逻辑进行运行。

以上三个部分在前两次作业中为三线程,第三次作业中有三个电梯线程和前两个线程,共计五个线程。

1.1 多线程协同

这部分主要讲三个部分如何互相协助,完成任务。

我们称输入处理部分为InputHandler,调度器部分为Scheduler,电梯部分为Elevator。以前两次作业为例(第三次仅仅是增加两部电梯,和前两次没有本质区别)进行说明。

1.1.1 InputHandler

我的InputHanler参考了给出的demo,使用while-true循环的方式进行读输入,当读入为null(即没有后续请求)时break退出while循环,并且修改标志位通知其他线程后续将没有请求输入了。

InputHandler通过将读入的请求存入与调度器共用的缓冲池,实现告知调度器请求的目的。每当InputHandler读入一个请求时,将调用“缓冲池.notify”,试图将调度器唤醒,通知调度器有新的请求。

1.1.2 Scheduler

我的Scheduler也使用while-true循环,但是每次分配任务完成后会根据结束标志位判断自己应当结束或者wait(等待后续请求)。因此Scheduler将不会因为盲目while-true占用过多的CPU时间。

当有新的请求进入时,Scheduler将会因为"缓冲池.notify"而醒来,进行任务的分配。

电梯通过“注册”的方式进入Scheduler,即在主线程中new出来的电梯使用Scheduler的方法进行注册,Scheduler内部有一个注册列表,内有所有电梯。

每当Scheduler将某一任务分配给特定的电梯x时,将会调用“x.notify”以只叫醒x电梯。这样也不会叫醒无关电梯而导致CPU时间浪费。

当Scheduler分配所有任务完成时,如果发现InputHandler已经将结束标志位置为true,则自己也会退出while-true循环,并且将自己与电梯沟通的结束标志位置为true,然后遍历注册列表的所有电梯进行唤醒。

通过这样的方式,可以保证所有电梯都能正常接收到Scheduler发出的结束信号,避免了有的电梯无限wait无人叫醒的情况发生。

1.1.3 Elevator

我的Elevator也使用while-true循环,但是每当电梯内没有待执行任务时将会根据Scheduler发出的结束标志进行wait或者退出。因此Elevator也不会因为盲目while-true而占用过多CPU时间。

电梯运行算法主要参考了LOOK算法,LOOK算法是磁盘调度算法的一种,这里我根据磁盘调度的LOOK我模仿了一个电梯调度的LOOK算法;

我采用LOOK算法的原因有以下几条:

  • 时间相对较短,即性能更好
  • 比较符合我们日常生活中电梯的行为
  • 不会出现饥饿显现(即某请求因为调度算法的原因产生长时间等待)

LOOK算法的具体实现就不在这里给出了,总之LOOK是和ALS稍有不同的一种算法,我个人认为LOOK算法在大量请求的情况下和ALS相比有较大的优势,因此采用了LOOK算法。

电梯将会在自己没有待处理任务时进入wait状态,并且会在自己收到任务时被叫醒。

电梯将会在自己没有待处理任务并且收到Scheduler的结束信号时结束,避免无限wait造成程序无法结束。

1.1.4 其他想说的

我的电梯在设计之初就采用了LOOK算法,这不仅仅是因为我早就猜到(知道)后续会需要捎带请求,更是因为我深知傻瓜电梯实际上是没有意义的,真正的电梯必须有自己的效率。

并且如果我第一次就写好LOOK算法的话后面就可以摸鱼了!

因此我的第五次作业就已经是LOOK算法的电梯了。拜此所赐,我第一周de了不少bug,但是后两周(尤其是第六次作业的时候)我确实轻松了不少。别的不少同学都需要重新思考自己的数据组织,而我除了加个ARRIVE几乎什么都不用干!

1.2 多线程同步(控制)

多线程程序面临的最大的问题之一就是线程同步。所谓线程同步就是所有线程对于共同的访问资源,需要同步信息。在多线程程序中,经常出现read-and-write等操作,因此需要注意共享资源的访问控制。

Java提供了synchronized关键字以实现简单的线程同步。我的程序中使用并且也只使用了synchronized关键字来实现这部分的需求。

在我的程序中,有两部分线程共享资源,一个是初始请求池,另一个是电梯待处理请求池。初始请求池可以由Scheduler和InputHandler操作,分别有读和写需求。

因此我在他们访问这一资源时首先使用synchronized关键字“锁住”这一资源,再对资源进行读写操作。

在电梯的待处理请求池的处理上,我也采用了类似的方式,即封装好的方法内部有synchronized关键字锁住的操作对象,以此保证线程安全。

1.3 自己的思考

在线程同步的学习中,关于需要考虑线程安全的地方,我自己总结了一下几个关键点:

  • 不同线程访问(修改)同一共享资源
  • 对于这次访问有迫切的绝对需要获得正确状态的需要

对于第一点,我们都可以很好地理解。但是对于第二点,我有自己的一些想法:

假设我们有两个人使用同一账户存取款,我们不妨设为A和B;A的操作是向账户里存入100元,B的操作只是读取账户内现有金额,并且在金额大于500时去吃饭。

根据上述设定,A线程的操作是不停向账户内存入100元,每存入一次休息一会;B线程的操作是不停访问账户,当金额大于500时去吃饭。

假设B对于去吃饭并没有迫切的需要,也就是说B并不会因为某次该读501时读到401而饿死,那么我认为B在访问账户内容的时候其实是不必上锁的。

也就是说,假设我们的线程对于某个共享资源的状态没有迫切的需要(即不是特别在意实时反应的那种),只是“日常访问”的话,那么其实对于读操作来说没有必要必须锁住共享对象。


2 基于度量分析自己程序的结构

在这部分中,我们将第5、6、7次作业分别讨论。(长图警告!(也可能是长列表))

注:

  • ev(G)  Essentail Complexity
  • iv(G)   Design Complexity
  • v(G)    圈复杂度

2.1 第五次作业——傻瓜电梯(我的是LOOK电梯)

2.1.1 复杂度分析

总体来看在几个比较大的方法的复杂度还是偏高的,值得反思,是否可以拆分为更多的方法?

2.1.2 类图

类图中可以看出,主要有三大线程,和Main线程。Main负责完成启动其余线程的工作。

StopFlag和Person是为了方便,自己封装的两个类。(StopFlag就是结束信号,Person是电梯内的人)

2.1.3 SOLID原则

SRP单一责任原则:实现较好

OCP开放封闭原则:实现较好

LSP历史替换原则:由于没有继承,不考虑

ISP接口分离原则:由于没有使用除Runnalbe以外的接口,不考虑

DIP依赖倒置原则:实现较好

2.2 第六次作业——ALS电梯(我的还是LOOK电梯)

2.2.1 复杂度分析

2.2.2 类图

2.2.3 SOLID原则

SRP单一责任原则:实现较好

OCP开放封闭原则:实现较好

LSP历史替换原则:由于没有继承,不考虑

ISP接口分离原则:由于没有使用除Runnalbe以外的接口,不考虑

DIP依赖倒置原则:实现较好

2.3 第七次作业——蛇皮电梯x3(我的是LOOK电梯x3)

2.3.1 复杂度分析

2.3.2 类图

2.3.3 SOLID原则

SRP单一责任原则:实现较好

OCP开放封闭原则:实现较好

LSP历史替换原则:由于没有继承,不考虑

ISP接口分离原则:由于没有使用除Runnalbe以外的接口,不考虑

DIP依赖倒置原则:实现较好

2.4 总结

实际上,我的三次作业的类就没有变过。所以三次的类图和SOLID分析是同样的。

主要是内部的方法实现发生了改变。不过实际上应该通过继承的方法来实现新的设计。


3 分析自己程序的bug

实际上,我的程序bug基本都在自测阶段处理掉了。

不过,在强测和互测阶段出现过一个超时bug,原因是因为我的电梯在转向的过程中处理状态发生了问题(第二次电梯作业我做了少许修改,结果导致了这个bug)。

不过还是有很多已经被我私下处理掉的bug需要注意的,主要有一下几种:

  • 过早停止的电梯拒载,解决方案:设置StopFlag和合适的算法,使电梯在执行完所有任务时才会停止;
  • 电梯无限wait无法停止,解决方案:在Scheduler结束时叫醒所有电梯,防止电梯wait但是Scheduler停止,无人叫醒电梯导致bug;
  • 早期实现中,我使用暴力轮训导致CPU超时,解决方案:使用notify-wait方式防止无用线程无限被调用,减少CPU时间

4 分析自己发现别人bug采用的策略

实际上,我这几次作业并没有认真hack别人的电梯。一方面由于自己有很多其他事情要干,活跃度就交给roommate吧!另一方面是因为我真的不想hack了。

实际上我和别的同学交流过程中发现,不少bug都是由于线程安全导致的。对于此类bug,我们需要着重观察对方程序对于共享对象访问的处理。

另外还有一种bug就属于真正的程序逻辑bug,此类bug大多因为程序员设计漏洞导致。对于这种bug,我们需要真正理解对方想要实现的算法,并且把自己的思考和对方的实现进行对比来发现。


5 心得体会

经历了电梯系列作业的洗礼,我们都有不少收获。

先说关于多线程编程调试的问题,多线程程序难就难在难以调试和复现bug。在这样的情况下更需要我们使用清晰的程序逻辑,方便自己通过“走查”的方式分析自己的bug。

多线程程序另一个重要的问题就是线程安全问题。我认为我们在做设计的时候有两种思路:

  1. 使用自己封装(别人封装)的线程安全类
  2. 在程序中注意线程安全,使用synchronized关键字等

我认为第一种方式是一种比较稳妥的方式,虽然我实际上是使用的第二种方法。事后我也考虑的自己的设计进行了反思。第一种确实是优于第二种的。

因为第一种线程安全类在使用的时候不是需要时刻注意的,并且是一种一劳永逸的方式。但是第二种在你编写新的部分的时候,必须也考虑其他线程是否会使用。

在设计原则上,我们应该坚持SOLID原则,因为这样的程序是易于维护、扩展的。另外我们应当善用单例模式、观察者模式、工厂模式等。以上几种模式都是为了更好地扩展维护的。

以上

转载于:https://www.cnblogs.com/heyedan/p/10742265.html

BUAA_OO_博客作业2——多线程电梯之旅相关推荐

  1. BUAA_OO_博客作业3——规格

    BUAA_OO_博客作业3--规格 • 梳理JML语言的理论基础.应用工具链情况 JML是java modeling language的缩写,是一种描述性质的语言.有一定的语法规则. 这种语言被用来描 ...

  2. 【BUAAOO】第四次博客作业

    [BUAAOO]第四次博客作业 说点闲话 繁忙了一个学期的面向对象课程终于快要正式结束了,虽然忙,却苦乐参半.许多"第一次"献给了OO--第一次熬夜,第一次高强度代码训练,第一次独 ...

  3. 【面向对象设计与构造】第一次博客作业

    [面向对象设计与构造]第一次博客作业 一.程序结构分析 1. 第一次作业 类图 由于第一次作业难度较低,实现起来也不需要很复杂的算法,因此在编写程序的时候只建立了两个类,Main类主要负责多项式的读入 ...

  4. OO第三次博客作业——规格

    OO第三次博客作业--规格 一.调研结果: 规格的历史: 引自博文链接:http://blog.sina.com.cn/s/blog_473d5bba010001x9.html 传统科学的特点是发现世 ...

  5. 作业二:个人博客作业内容:需求分析

    作业二:个人博客作业内容:需求分析 怎样与用户有效沟通获取用户的真实需求? 访谈,正式访谈系统分析员将提出一些事先准备好的具体问题:非正式访谈中,分析人员将提出一些用户可以自由回答的开放性问题,一鼓励 ...

  6. C语言第二次博客作业---分支结构

    C语言第二次博客作业---分支结构 一,PTA实验作业 题目1.计算分段函数 本题目要求计算下列分段函数f(x)的值 1.代码 double x, result;scanf("%lf&quo ...

  7. OO--第三单元规格化设计 博客作业

    OO--第三单元规格化设计 博客作业 前言 第三单元,我们以JML为基础,先后完成了 PathContainer -> Graph -> RailwaySystem 这是一个递进的过程,代 ...

  8. DS博客作业08--课程总结

    1.当初你是如何做出选择计算机专业的决定的? 当初选择计算机专业是因为比较喜欢电脑,有事没事喜欢慢慢摸索一些关于电脑的乱七八糟的东西.因为接触电脑比较早,所以对电脑的操控什么的都比较熟练.当然我也特别 ...

  9. 2018091-2博客作业

    此作业的要求参见https://edu.cnblogs.com/campus/nenu/2018fall/homework/2101 1. 建博客(1分) 在 cnblogs.com (博客园)建账号 ...

最新文章

  1. mysql切换到使用openssl_OpenSSL可以用来调试到MySQL服务器的SSL连接吗?
  2. 基于tcp协议的socket
  3. Apache服务器错误问题Internal Server Error
  4. 使您的Spring Security @Secured注释更干燥
  5. mysql typeindex_explain mysql的type字段,索引的类型
  6. java中string的方法_java中String的常用方法
  7. SpringBoot整合Redis 主从复制_01
  8. Python序列基本操作(三)
  9. matlab 绘花,【原创】使用matlab绘制菊花和玫瑰花
  10. Mybatis简单入门及配置文件标签详情
  11. oracle plsql创建表空间,Oracle在PLSQL Developer上创建表空间和用户脚本 - 龙卷风的日志 - 网易博客...
  12. Ubuntu进入pycharm创建的虚拟环境的方法(以及如果你安装了anaconda等其它修改了环境变量的东西该怎么进)
  13. Linux Performance Observability Tools
  14. Comparable 与 Comparator 比较
  15. 群论基础速成(6):五大著名群族
  16. c语言n层文字塔程序的结构图,精馏塔中由塔顶向下的第n-1,n,n+1层塔板,其气相组成关系为( )...
  17. Bootstrap(10) 进度条媒体对象和 Well 组件
  18. C语言编写一个函数,实现计算并返回一个整数的平方(或立方)
  19. Icon是什么,在线实现图片转Icon的方法
  20. 怎样在oracle中加下划线,Oracle使用like查询时对下划线的处理方法

热门文章

  1. 树莓派 3B 入手 基础篇(一)
  2. 文本框/文本域,部分文字不可编辑(输入/删除等)。
  3. outlook中实现邮件自动回复
  4. 共享文件服务器实施方案,企业文件共享服务器搭建思路
  5. 1.《如何构建敏捷项目管理团队》之成为好教练(摘要)
  6. “你的电脑将在一分钟后自动重启”、卡在“欢迎”界面、网络/声音图标显示红叉、无法连接到服 务、网络图标消失、开机需要同时使用用户名和密码登录等的解决方法
  7. python(数据分析)第3天:坐标轴显示中文,旋转
  8. 宜人贷蜂巢API网关技术解密之Netty使用实践 1
  9. sql serve2008的增删改查操作
  10. 王者荣耀s12赛季服务器维护,王者荣耀S12赛季更新内容 排位不掉星方法