• OO第二单元——多线程(电梯)


    OO第二单元——多线程(电梯)

    综述

    第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深。本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题。

    第一次作业——单部电梯(无调度)论述

    第一次作业,是写一部电梯的模拟器,指导书提供了一个极为基础或者说不叫算法的算法,只需要将人送到正确楼层即可,对于时间、性能没有任何要求,但也是我们第一次尝试多线程,开始写代码时对于wait(),notify()函数并不是十分了解,导致出现了各种线程不安全,或是出现死锁的状况,代码写得也比较的复杂,架构并不是十分清晰。

    性能分析

    方法复杂度

    明显可以看出run方法复杂度较高,其他方法为正常水平。

    类复杂度

    由于没有任何性能要求,我采取了接完一个人再去接另一个人的方法,效率极低,但可以保证正确性,建立了两个线程,一个Producer线程,负责读取输入,并放入共享队列中,另个一个Consumer线程,负责处理共享队列中的指令,并指导电梯上下移动,而在这次作业中我将电梯和Consumer写在了一起,也就是没有调度器线程,而只有生产者、消费者。

    在后期,我也遇到了一些问题,比如结束程序的问题,我在Ctrl D后,并不能结束进程,而是卡在了一些死循环或是wait里,最终我也在用了信号量的情况下多加判断,结束进程。

     

    第二次作业——单部电梯(ALS调度)论述

    第二次作业,与第一次作业要求并无太大差别,只是增加了地下楼层,也就是负数楼层, 但在性能上有所要求,指导书给出了ALS算法,即捎带算法,在电梯运行过程中捎带路过楼层的乘客,相较于第一次作业的FAFS算法,有了很明显的进步,性能大大提高,难度也有所提高。输入输出接口仍然给好了jar文件,无需我们担心。下面先看一下IDEA对于我代码的分析。

    类图

    方法复杂度

    复杂度较高的几个方法中都包含了较多的if条件判断,为确保正确性,只能以性能换正确。代码设计并不是十分到位。

    类复杂度

    第二次作业,我按照指导书使用了ALS算法,每到一个楼层,我会先让进程sleep(400),以度过开关门时间,然后检测电梯的等待队列中,是否有当前楼层的乘客在等待,若有则进入电梯乘客队列,接着遍历电梯乘客队列,将到达楼层为当前楼层的乘客送出电梯,每一次电梯运动前,都会更新一下当前的目标楼层,目标楼层也会综合考虑等待队列中的出发楼层,然后电梯根据目标楼层选择向上运动或向下运动,每运动一层为一个周期。

    我认为这次作业的难点也就主要在调度一块,若是调度算法太差会导致Real-Time-Limited-Exceed,若是暴力轮询不sleep()会出现CPU-Time-Limited-Exceed,这也是我后面自己试出的一个小技巧,由于对于wait(),notify()两个方法的理解不到位,我的电梯内部采用暴力轮询,以防止错过任何一条指令或是进程在等待i队列还有指令的时候就结束。 但是我在获取指令的进程中采用了notify(),以唤醒get()方法中的wait()。

    第二次作业我并不是十分成功,由于个人架构太失败,导致调度环节出问题,电梯运行方向并不会正确的改变,导致在强测时挂了多组数据,未能进入互测,这次作业我吸取了教训,知道一个不正确的架构,一定写不出一个正确的程序,在开始码代码前一定要考虑周全,想到程序运行的每一方面,尤其是结束进程方面,一定要在开始之初就设置好信号量,做好规划,而不能等到写完程序后在程序内部打补丁,强行结束程序。

     

    第三次作业——三部电梯(自行调度)论述

    第三次作业提高要求,设置为三部电梯,且每台电梯能够到达楼层互不相同,也就导致了需要乘坐两台电梯才能到达目标楼层的操作,且每台电梯运行速度,最大承载量也不相同,一开始我想使用继承,一个父类电梯包括电梯所有操作,然后用子类修改电梯的各种数据,造出三台电梯,但后来我发现了更加直接的方法,使用构造方法来确定电梯的几个参数,无需使用继承,且继承run方法我也不太确定是如何运行的。下面先看下IDEA的分析。

    性能分析

    类图

    方法复杂度

    复杂度高的函数如Tray.get(),因为三台电梯不同,导致获取的目标也不同,也就是用了if判断语句,增加了复杂度,但以后可以使用数组,传入index以减少if的使用,而在获取aimFloor一块,依然复杂度极高,毕竟有多种情况,我只能使用if判断,手动判断情况。

    类复杂度

     

    第三次作业我还是使用了生产者-消费者模式,由于会出现一台电梯无法送达的情况,我选择在当初将指令放入等待队列时,便拆分好,我会考虑所有的中转楼层,选取其中路线最短的中转楼层,然后将前一条指令先放入恰当的电梯中,后一条指令放在我的Monitor线程中,这是我新开的一个监视者线程,负责查看出电梯人员中,是否有id与监视者的等待队列中的id相同,若有,则该等待队列中的指令放入适当的电梯等待队列中,其他的架构都与第二次作业都差不多。

    第三次作业的强侧表现也不太好,在这里我分享一下我debug的经历,我频繁出现CPU-Time-Limited-Exceed的问题,根据以往的经验判断,一定是暴力轮询没有sleep导致的,但我也并不是很清楚是哪一个while循环出现的暴力轮询,于是我才用输出debug的方法,在每一个while循环内部都都增加了输出,“XXX(类名):XXX(方法名) Here!”, 以判断哪一个while出现暴力轮询,当然输出结果极其恐怖,一个txt文件里面有25w+行的输出,显然找到了bug所在,但是自己在课下测试时并没有仔细测试。

    这一次的经历也教会了我使用JProfiler,可以在Thread模块,查看是否有block,若是出现block,即代码内容有问题,然后使用输出debug,找到bug所在。

    同时,在研讨课上,也有同学分享了System.error方法,可以输出错误数据,以后debug时可以尝试。

  • 相关阅读:
    1046 Shortest Distance (20 分)(模拟)
    1004. Counting Leaves (30)PAT甲级真题(bfs,dfs,树的遍历,层序遍历)
    1041 Be Unique (20 分)(hash散列)
    1036 Boys vs Girls (25 分)(查找元素)
    1035 Password (20 分)(字符串处理)
    1044 Shopping in Mars (25 分)(二分查找)
    onenote使用小Tip总结^_^(不断更新中...)
    1048 Find Coins (25 分)(hash)
    三个故事
    领导者的举止
  • 原文地址:https://www.cnblogs.com/JordenQiao/p/10761531.html
Copyright © 2020-2023  润新知