热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

北航面向对象2022第二单元总结(Elevator.exe已停止运行…)

您的电梯已停止运行(NO!)这里是BUAAOO第二次单元作业总结博客。第二单元是BUAA的电梯模型,今年的电梯比起往年看起来友好很多。在第五次作业上手时,因为是第一次搭建相关逻辑,

您的电梯已停止运行(NO!)

这里是BUAAOO第二次单元作业总结博客。第二单元是BUAA的电梯模型,今年的电梯比起往年看起来友好很多。在第五次作业上手时,因为是第一次搭建相关逻辑,并且对于多线程构造和运行状态处于懵懂阶段,可能会遇到一些麻烦。(看到往年第一次的作业是傻瓜电梯的时候,我还在想今年的电梯上手真困难)不过在第六、七次作业中,一些增量迭代显得不那么困难,仅仅只是从单纯的纵向电梯转为了横纵交错的电梯,最后实现了可换乘的电梯运行mode。总而言之,三周的作业写的十分充实,并且让我感受到了多线程别致的“魅力”。




HW_5

本人的三次电梯架构基本完全基于第五次作业的base,即使增量也没有进行大规模的重构。因此在这里,我先进行电梯整体架构设计的阐述。


同步块选择及锁的设计

首先是同步块的设置和选择,在没有参考实验代码时,我尝试自主设计同步块和有关锁,然后在自信满满地上交后,发现电梯光荣地死锁了。之后,我好好研究了一下第三次实验的代码,发现一个很有意思的事情:所有的操作线程wait和notify的内容都放在了RequestQueue中,在这样的设计下,同步块和锁集中于此,各个线程相当于把这里作为了一个公共空间,在想要访问该内容时便采取“等待”、“进入”、“使用完让行”的操作。这样的设计可以很好地满足线程安全的原则,并且也能解决我的设计问题。因此,我将每个电梯内的服务队列所在类设置为同步块集中区,并且使用synchronized关键字进行互斥维护,此时,对每个电梯来说,线程安全就得以满足。


调度器设计

在任务分配,也就是调度器方面,我直接用主线程来进行任务的输入操作,并没有单独的设置一个新的线程来进行处理。任务的处理以及调度的策略集中存放在TaskTower这个类内,用任务塔作为一个中介,将任务由输入线程转入电梯运行线程。


整体框架

首先是hw5的UML类图:

由于第二单元刚开始,大家提到生产者-消费者模式比较多,因此我也对应着做了一点调整。具体来说,输入作为生产Passenger,也就是任务的生产者;电梯内Strategy存有待做任务和正在做的任务,对应两个队列,电梯运行时会获取内部的Passenger加以送达,也就是任务的消费者。具体到电梯内部运行,和标准流程一样,用ALS策略,在上升下降的过程中,如果电梯没有人,就指定在本电梯外部等待的第一个Passenger为主任务对象,以它为目标。如果内部存在任务,就更换主任务对象为内部第一个存入的任务。


程序BUG

本次在公测互测中分别出现了一个bug,一个对应线程永久等待不会结束的问题,导致最后rtle。主要原因是在输入线程结束,setEnd位时,没有处理好互斥原则,导致设置End位在等待以前结束,而唤醒结束后才进入wait。另一个对应输出线程不安全的问题,导致时间戳获取与实际输出顺序颠倒。

由于第一次互测经历了重测,我在互测中总共发现了其他人的两个bug,刚好对应我自己的两个问题(同组人没想到也犯了一样的错误)。在互测中,我自己使用数据生成器生成数据来进行hack,不好构造数据时,压力测试还是很好的选择,毕竟没有重测之前,本组就只有我测出了一个bug。




HW_6

第六次作业主要增加了在楼座间可以循环运行的横向电梯,以及可以新增电梯的操作,需要采用新的设计思路来管理。但总体上来说,可以和hw5采用一样的方法来处理。


调度器增量

由于我们需要满足新的请求:增加电梯请求。因此,我们要在调度方面采取一定的策略来选择新加入的电梯。我并没有采用电梯自由竞争的方式,而是按照课程组给出的标程,直接顺序循环遍历可以处理任务的电梯,保证均匀分配。事实证明,在强侧中我使用标程也取得了很不错的效果,不仅完全不存在超时的问题,在个别任务上还达到几乎满分的速度。


框架升级

接下来是hw6的UML类图:

可以发现,我改进了电梯管理类来方便新增的电梯管理。这里主要谈一谈新增横向电梯的设计策略。总的来说,从UML图中也可以看出,我设计时基本参照了纵向电梯的设计思路。主要区别在于,横向电梯把顺时针看作上升,把逆时针看作下降。同时,用起始楼座之差的绝对值和符号来判断向哪个方向走最近。具体如下:

boolean upOrDown = !(endID - stID > 2 || (endID - stID <0 && endID - stID > -3));

程序BUG

本次在公测互测中没有出现bug,本次测试中,同组的大家基本都考虑到了第一次出现的各种线程安全问题,并且在线程结束方面也下了狠手。并且新加电梯的个数有限,不太容易发现bug。我还是采用随机生成数据的方式来进行hack。但是我采用了“新的实践方式”,我把自己前几版错误的代码拿出来进行错误代码认证,并且将产生错误的生成数据归纳到对应的bug下。果然“实践是检验真理的唯一标准”。




HW_7

第七次作业主要增加了电梯可设置参数的要求,以及乘客可以跨楼座跨楼层移动的请求。实际上前者在我们的设计中很好体现,只需要添加对应的参数输入即可。后者我虽然参考了课程组给出的流水线代码,但是实际上做出来是一个分段式的三段请求链结构,分成纵向—横向—纵向来进行任务运行。


调度器改进

由于我们需要满足电梯换乘,在我的设计中,我在调度器中增加了换乘逻辑。具体来说,我将请求分为两部分,一部分对应直上直下的固定请求(hw5),一部分对应跨楼座的请求。对于后者,每当有新任务进入调度器,就分析在何处跨楼层,并让乘客记住自己将换乘的电梯及楼层,并进行任务投喂。(我不是采用的电梯获取请求,而是调度器投喂请求的方式,也就是不会自由竞争,单纯进行均衡分配)


框架进一步升级

接下来是hw7的UML类图:

可以发现,我改进了电梯管理类来方便换乘时的电梯管理,并且增加了一个单例对象来方便换乘。事实上,对于乘客来说,如果需要换乘,他总共会有三个运动阶段,在每一次被投喂给电梯前,我会先更新乘客的运行状态,修改好起始、终止楼层,起始、终止楼座。因此,当所有阶段完成后,乘客就会被移除任务清单。

另外值得一提的是,我借鉴了实验exp4_2中的请求清算结束方案,在输入线程结束输入任务后,被视为新的结算线程运行。每当有乘客请求投入时,RequestNum自增1,每当类似上述所说的乘客被移除任务清单后,触发任务结算,并将RequestNum自减1,最后,所有任务都被处理完,并且输入的线程显然也消亡了,就会触发整体模块的结束,顺利退出程序。

我们把最后设计好的情况的四个时序图展示如下,分别对应主线程(启动线程)、输入调度、以及横、纵电梯的运行逻辑,最后是请求结算逻辑。


程序BUG

本次在公测互测中出现1个bug,但我自己在之后又整理出一个bug.前者是致命性bug,由于单元测试不充分,发现只要来同座运输请求(hw5),就会由于强行调用第二部分的方法导致空指针异常(很心痛,很值得反省)。后者是由于换乘导致的线程互斥问题,主要是对于迭代器遍历同时修改,造成了不可知的影响。

在互测中,我针对代码运行逻辑试图卡运行时间来hack,构造了边界情况:在输出截止时间输入最大数据量最远抵达路径的乘客。成功造成了两次hack;另一方面,和hw6一样针对自己的代码生成了错误数据代码,又造成了两次hack。最后总计造成了4次不同的逻辑hack,有比较好的效果。




心得体会

通过本单元学习,可以感受到助教们的满分热情(bushi)。我们在第一次作业就直接采用ALS策略电梯,构建起对多线程的整体框架认识,快速上手。后面两次作业看似加了不少新内容,但做起来也并不复杂,只要有了第一次作业迈上台阶的基础,就能很好地处理多线程中的各种问题了。(被hw5暴露的各种bug很好地让我认识了多线程中可能出现的各种问题)

另外,我本人不太擅长自己构建一些新鲜的策略算法来进行处理,于是按照课程组给出的标程逻辑一步一个脚印走完了全程。总的来说,由于多线程调度的不确定性,我使用标程的代码最终取得的效果并不赖,完全不存在超时的问题,也从未产生过轮询的现象,没有出错的得分点分数也不错,这让我感到一丝丝自己完成一份任务的成就感。

撰写本总结博客时,回味整个月的思考、迷茫、再建、讨论、测试、修复,真是百感交集。留下的,不只有经验教训,更有继续勉励的信心。

精感石没羽,岂云惮险艰。”OO课程已过半,后面就,再接再励吧。



推荐阅读
  • Unit4博客&课程总结Unit4作业的架构设计本单元作业的设计我分为了三个模块处理:模型构建+预处理+任务函数,前两部分即为整个图的完整构建,第三部分即为实现题目要求的查询方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 深入理解线程、进程、多线程、线程池
    本文以QT的方式来走进线程池的应用、线程、进程、线程池、线程锁、互斥量、信号量、线程同步等的详解,一文让你小白变大神!为什么要使用多线程、线程锁、互斥量、信号量?为什么需要线程 ... [详细]
  • 第四单元和课程总结:简单的架构设计意识
    一、第四单元架构设计总结第一次作业由于需要按名查找类图模型,于是建立&amp;quot;Class&amp;quot;类进行管理由于方法具有参数导致类中存在二级结构 ... [详细]
  • Spring MVC 浅谈
    大学时写的的文章,当时文章水平略差,大家见谅。MVC这个词儿,最早的定义应该是作为一种软件架构设计模式出现在软工里面的,即使用model、view、controller来设计及定 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了Java虚拟机中的垃圾收集器,包括年轻代收集器Serial收集器、ParNew收集器、Parallel Scavenge收集器,以及老年代收集器Serial Old收集器、Parallel Old收集器和CMS收集器。对每种收集器的算法和特点进行了详细解析,希望对读者有参考价值。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 数据结构与算法的重要性及基本概念、存储结构和算法分析
    数据结构与算法在编程领域中的重要性不可忽视,无论从事何种岗位,都需要掌握数据结构和算法。本文介绍了数据结构与算法的基本概念、存储结构和算法分析。其中包括线性结构、树结构、图结构、栈、队列、串、查找、排序等内容。此外,还介绍了图论算法、贪婪算法、分治算法、动态规划、随机化算法和回溯算法等高级数据结构和算法。掌握这些知识对于提高编程能力、解决问题具有重要意义。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • java线程池的实现原理源码分析
    这篇文章主要介绍“java线程池的实现原理源码分析”,在日常操作中,相信很多人在java线程池的实现原理源码分析问题上存在疑惑,小编查阅了各式资 ... [详细]
author-avatar
M7y4C8r2a6z4y
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有