面向对象电梯系列总结

摘要:
然后,根据每个队列是否为空,很容易确定线程是否结束,这可能会导致死锁。我使用在对象之间发送消息的方法来退出线程。调度器判断计数(指示拆分请求的数量)是否为0,以及主线程是否结束自身,并向电梯线程发送调度器已结束的消息;电梯线程根据其任务队列以及电梯轿厢中是否有人来决定是否结束。判断电梯运行时方向是否有请求的功能相对复杂。编写了一个python程序来模拟输入。

一. 设计策略

1. 架构设计

  三个线程:电梯,调度器,主线程(输入线程), 采用worker thread,生产者消费者模式。和同学讨论,发现有的观点认为:调度器更像是一个功能的集合,类似一个函数,不像是一个主体,而且线程越少bug一般而言越少,于是调度器不做为线程。也挺有道理。架构图如下:

面向对象电梯系列总结第1张

   其中task使用ConcurrentHashmap实现,对其方法没有加synchronized, request使用普通List实现, 对其public方法都加了synchronized。这两个类作为被多个线程访问的类,进行了同步控制。

main向request中放入请求,调度器从request中拿出请求并分配给一个电梯(放进该电梯的task处),电梯从自己的task中拿出请求并执行,car是电梯的轿厢,代表电梯内的请求。此种实现方式不是电梯去向调度器要任务,而是调度器主动分配任务给电梯。整个架构耦合度较低。

2. 调度算法设计

  将调度分解成两个独立的过程:分配电梯,分配到达楼层。

  分配电梯的算法如下:

 面向对象电梯系列总结第2张

  其中有容量指task.size+car.size<capacity,电梯只捎带同方向的。

  分配到达楼层:尽力而为,使到达楼层尽可能靠近请求的目的楼层。

  在强测中本人的调度算法表现的还不错,缺点是在较少请求的情况下,没用使负载较为均衡。

3. 线程如何结束

  只设一个输入结束的全局信号,再根据各个队列是否为空 来决定线程是否结束很容易造成死锁,我采用对象间发送消息的方式来退出线程。main线程在输入结束后通知scheduler线程main已结束,然后结束自己;scheduler判断count(表示已拆分请求个数)是否为0以及main是否结束来结束自己并向电梯线程发送scheduler已结束的消息;电梯线程根据自己的任务队列和轿厢内是否有人来决定是否结束。综上,main退出决定了scheduler退出,scheduler退出决定了elevator退出,整个过程非常有节奏,不再存在有进程没被唤醒或者死锁的情况。

二. 度量分析

1. 三次作业的UML图

面向对象电梯系列总结第3张

面向对象电梯系列总结第4张

面向对象电梯系列总结第5张

2. 以第三次作业为重点,分析经典度量

类内部的复杂度: 调度器和main类比较重

 面向对象电梯系列总结第6张       

类间的依赖度:不高

面向对象电梯系列总结第7张

方法复杂度分析(仅保留值不是很低,起主要作用的方法):调度器分配电梯的函数,电梯运行时判断所在方向是否有请求的函数比较复杂,在情理之中。

面向对象电梯系列总结第8张

对类与方法的代码规模进行统计:

 面向对象电梯系列总结第9张

3. 第三次作业的时序图

面向对象电梯系列总结第10张

4. SOLID原则

  单一责任原则: 电梯负责运行,调度器负责分配请求,其他辅助类功能都单一,符合该原则。

  开放封闭原则: 第一次作业扩展性不好,导致第二次作业直接重构了,重构后的架构就是上图所示架构,第三次作业在大的方面只修改了调度器以及电梯类的少部分代码。

  里氏替换原则,依赖倒置原则,接口分离原则:程序中没有继承关系和接口。

三. bug分析

  三次作业均未在强测中出现过bug。

  评测机:写了个python程序来模拟随时间的输入,用脚本构建了一键测试程序,测试的自动化极大地提高了开发效率

  debug: 线程内部的功能bug可通过ide来找,但一旦涉及线程间的交互,就只能使用printf了。当然,熟练使用打印日志的方式后,速度可以比IDE更有效。在code阶段就在关键位置,可能有bug的位置,存在复杂计算,逻辑比较混乱的地方加上打印日志的代码,debug阶段一看日志就能迅速定位错误了。强烈推荐Hansbug写的分级日志输出工具:https://github.com/HansBug/debug_logger,非常好用,极大地提高了打印日志的友好度,真正让我领略到了输出调试的威力

  如果多线程的测试还靠ide, 或者肉眼去看,那可能真会出现”多线程玄学“。当深刻掌握JVM的内存模型,共享对象的可见性和线程的同步性以及使用日志调试后,多线程其实也跟单线程一样,只不过稍微麻烦一些。

四. 发现别人的bug

  虽然没有互测,但测试自己程序时还是形成了一套方法。

  首先对单个模块进行功能测试,再集成测试。测试用例既要有普遍的,也要有专门针对优化算法设计的,数量要足够多。很多bug都是出现在电梯满了的时候。

五. 心得体会

1. 线程安全

  什么是线程安全,如何保证安全大家都懂,我就不赘述了,在这里谈一谈大家容易忽略的问题并推荐几篇文章:

(1)从Java多线程可见性谈Happens-Before原则(链接)

  在现代操作系统上编写并发程序时,除了要注意线程安全性(多个线程互斥访问临界资源)以外,还要注意多线程对共享变量的可见性,而后者往往容易被人忽略。这篇文章非常清晰地解释了JMM(java的内存模型)中的happens-before原则,读完能够深刻透彻地理解该原则如何解决多线程对共享变量的可见性问题(包括缓存一致性和重排序)。

  虽然可见性问题在这几次作业中我并没有遇到,但应该引起注意。

(2)关于double-checked locking的讨论

  双检查锁这个技巧看起来精巧,但却是丑陋的,由于JVM的优化越来越完善,在现代工程开发中DCL已经被废弃了。

2. 设计原则

  UML建模:在进行项目的时候,通过使用 UML 的面向对象图的方式能够更明确、清晰的表达项目中的架设思想、项目结构、执行顺序等一些逻辑思维。这几次的作业比较简单,架构也很明晰,没必要先画出UML图后再去码代码。尽量减少类与类之间的依赖,可以通过消息机制完成类间的通信。

免责声明:文章转载自《面向对象电梯系列总结》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇asyncio之Coroutines,Tasks and Future药方大而全,无法调节人体的阴阳平衡--滋阴药寒凉药误用的危害(转载)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Java内存模型(JMM)总结

Java内存模型(JMM) 我们常说的JVM内存模型指的是JVM的内存分区;而Java内存模型是一种虚拟机规范。 Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM),用于屏蔽掉各种硬件和操作系统的内存访问差异,实现让Java程序在各种平台下都能达到一致的并发效果,JMM规范了Java虚拟机与计算机内存是如何协同工作的:...

Java(多)线程中注入Spring的Bean

问题说明 今天在web应用中用到了Java多线程的技术来并发处理一些业务,但在执行时一直会报NullPointerException的错误,问题定位了一下发现是线程中的Spring bean没有被注入,bean对象的值为null。 原因分析 web容器在启动应用时,并没有提前将线程中的bean注入(在线程启动前,web容易也是无法感知的) 解决方案 方法有...

Handler 机制(一)—— Handler的实现流程

   由于Android采用的是单线程模式,开发者无法在子线程中更新 UI,所以系统给我提供了 Handler 这个类来实现 UI 更新问题。本贴主要说明 Handler 的工作流程。 1. Handler 的作用 在Android为了保障线程安全,规定只能由主线程来更新UI信息。而在实际开发中,会经常遇到多个子线程都去操作UI信息的情况,那么就会导致U...

10 : mysql 主从复制

延时从库 主从复制很好的解决了物理损坏,但是如果主库有个误删除写入的操作怎么办? 正常情况下从库也会同步这个错误的,企业中应该怎么避免这个情况?这个时候就需要使用延时同步来解决: 延时从库?delay(延时)从节点同步数据。 对SQL线程进行延时设置。IO线程正常的执行。企业中一般延迟3-6小时 延时从库的配置过程:mysql>stop slave;...

WatchDog工作原理

Android系统中,有硬件WatchDog用于定时检测关键硬件是否正常工作,类似地,在framework层有一个软件WatchDog用于定期检测关键系统服务是否发生死锁事件。 watchdog的源码很简单,主要有两个功能 1监控system_server中几个关键的锁,原理就是在android_fg线程中尝试加锁 2监控几个常用线程的执行时间,原理就是在...

静态代理和动态代理的区别

什么是静态代理: 打一个最简单的比方,我现在想要学习,那么就必须得把书包拿过来,把书掏出来,准备好纸笔,然后开始学习,等学完了我还得收拾书,把书塞回书包里,还得整理一下书包,这是一个完整的学习的过程,但是我很懒,不想动弹,只想学习,那可能就得让妈妈帮我把书包拿过来,把书打开,我只管学习就好了,学完以后,妈妈再帮我把书整理好放回去.(我这是打个什么破比方.....