OOP第二章博客

OO第二次博客作业

(1)作业分析

三次作业在处理多线程的协同配合时都是使用将同步放在自己写的“线程安全类”(经测试有些许漏洞_,但是不影响结果就是了);

我个人倾向于把wait()和notify()等操作放在安全类里面,这样可以实现逻辑上的抽象,不会使得电梯的工作逻辑比较乱,一开始使用了Java自带的线程安全类,但是在终止输入、电梯、调度器时遇到了问题,所以最后还是自己乖乖写了一个自己的类,把request包装了一下,仅仅在里面加入了我自己关于中断的逻辑;

第一次和第二次都是把电梯线程都放在了main县线程开启,在判断退出时比较丑陋。


第一次作业是通过main函数轮询判断队列情况和电梯运行状态再通过interupt()函数打断电梯线程;整体设计上选择了安稳过活方式,即input不断将请求放在队列中,电梯不断处理请求,一次处理一个,标准的生产者消费者模式!类图结构如下:

电梯线程,输入线程和主线程三线程,一个当作缓冲区的queue,于是傻瓜电梯诞生了!


第二次作业是直接使用结束时将null加入到队列中作为一个结束标志,调度器取到null就会等当前电梯工作结束,再把电梯停止,自己再退出;

并且选择使用look算法,在每一层都将所有要进入电梯的人和要出去的人都接进来,踢出去。同时为了预测一下第三次作业的多电梯,采用了调度器将请求分配给电梯,输入将请求无脑push到队列中的做法,希望实现一定的解耦;

对于电梯,我设计了两个队列,一个是要进电梯的队列,一个是要出电梯的队列;输入和调度器共用一个队列,由调度器选择将合适的请求分配给电梯!

整体上的设应该说是逻辑比较清晰也比较稳定的,并没有在强测和互测中失利(当然这不代表完全没有漏洞,毕竟个人在设计时还有些不太理解多线程)


第三次的停止思路来自于ppt,再调度器中开了电梯线程,于是当调度器发现没有请求,并且三个电梯都闲置下来(即没有请求),这时停止全部线程;然而这里没做好,一开始只是通过判断队列情况来stop线程,于是就会有电梯队列空而“CLOSE-floor-name”没有输出,导致强测痛失一个点!┭┮﹏┭┮,之后改为判断电梯停止状态就通过了那个点;

但是,但是,但是,在另一个强测点又翻车了(Σ(っ °Д °;)っ)但是这个问题自己尝试一直无法复现,于是不知道该如何修复,而第二次提交又神奇的通过了,可能多线程bug便是如此,不经意间出现,不经意间翻车!结构如图

将ABC三个电梯分别继承自一个通用的电梯,同时将电梯的楼层抽象出来,电梯的队列设计还是源于第二次的,调度器和输入之间有一个缓冲区,调度器选择将合适的请求给某个电梯,然后这里就涉及到第三次作业的调度算法了:

为了方便,将请求包装为了person类

调度器选择将某个人分配给电梯,若可以直达,则直接放在相应电梯的进入列表中等待而不管是否满员了(这里是后来的测试表明不太好);若无法直达,则选择分配给可以进入的电梯,判断次序为ABC,之后再电梯里会进行送达楼层的选择;

这里通过分析,很容易得知这些情况,-3,16-20是A电梯的特殊点;2,4,6,8,10,12,14是B电梯的特殊点;3,5,7,9,11,13是C电梯的特殊点,这里尤为特殊的是3号楼层,与此配套的便是2号楼层和4号楼层,他们之间的达到比较特殊,需要折返!

当然,由于我把这些判断都放在了电梯的一个调度算法中,所以对于调度器来说不可见,它只需要负责自己前面的事情就好!

  1. 对于A电梯,不能直达的人会选择在1或者15层下楼;

对于B电梯,不能直达的人送到1或者15层(对于3-2,3-4这种单独考虑)

对于C电梯,不能直达的,会发现,经过前面的选择,此时只有从3层出发的才会到这里,所以分情况考虑即可,也是选择了1或者15层

对于3-4(3-2)这种,选择根据电梯的行进方向关联起来,便有四种情况;

以上是这次采用的算法,没有进行更多的优化,因为不太理解助教所说的那种数据结构的包装是什么意思,自己的尝试也发现漏洞百出,便不敢尝试了,怕影响正确性(只是思考了优化的思路)。


(2)度量分析及SOLID原则分析

第一次作业:

第一次作业的功能比较简单,而且算法是傻瓜,所以复杂度不是很高,而且时序图也不会很复杂;如上图

在SOLID原则上:

S:类的功能是唯一的

O:仅支持先到先得,傻瓜式调度

L:没有继承关系

I:第一次没有接口

D:无法复用,不太符合原则


第二次作业:

第二次作业的设计是开了三个线程,在复杂度上也做的不是很好,有2个函数的复杂度较高,究其原因是电梯双队列的设计导致的电梯代码复杂度提升,在逻辑控制上没有大问题,取消了忙等,采用wait和notify机制,所以cpu时间没有任何问题,但是需要考虑思索问题,所幸设计目前没有发现死锁;

SOLID设计原则:

S:类的功能是仍然是唯一的,但是电梯采用了双队列的设计,比较复杂

O:第三次作业成功沿袭(其实是不想重构,感觉设计的还可以)

L:仍未采用继承关系

I:使用了常数接口,用来共享常数

D:做的还是不够好,细节不太到位,不过第三次的重用角度看勉强及格


第三次作业:

第三次作业的设计采用了电梯的继承,分为三个电梯——A、B、C;

在复杂度上有些许不足,电梯的运行逻辑还是有些复杂,内聚有点大,其他的函数几乎没有问题;

在时序图中,主线程负责开启输入和调度器线程,而电梯线程都是在调度器中进行启动,这是为了结束时方便停止电梯,可以直接使用elevatorThread.interupt()函数;调度上电梯会把不能直达的人送回到调度器,所以停止的标志相对于第二次相对复杂一些,也是我出现问题的地方,需要同时满足四个条件(队列空,三个电梯等待)才可以停止全部线程。

SOLID原则分析

S:进一步抽象了很多的类出来,但是在功能上保证了唯一性

O:基于第二次的扩展写法,个人认为具有一定的开放性

L:多个电梯采用继承自一个基本电梯的思路,而且函数也全部复用或者重写,基本实现

I:常数接口

D:还是做的不好,调度方面是针对细节编程的


(3)bug分析

第一次作业因为比较简单,所以目前还没有发现问题;

第二次作业在采用了look算法并且做了较好的解耦后,也没有bug;

第三次作业有一个bug,在判断电梯的停止状态时选择了使用队列为空的方式,而使得会出现“CLOSE-floor-name”的语句无法输出;

在互测过程中发现的bug主要是在第三次作业,其中最多的就是3-2,3-4这类特殊的楼层请求,主要是没有解决好换方向的问题;

在周五的上机测试中,我个人在课下自己写时也遇到了一个bug:

我选择使用PV操作的原理来实现银行的算法,但是把存取操作都放在了PV操作的外面,即P-存取-V,这种形式,但是因为忽略了OO和OS的区别(或者说是锁机制和PV操作的区别),在JAVA中,这样会使得操作不被锁起来,但是因为逻辑处理不会产生数据冲突,却有这样一个问题,多个线程在锁区域wait等待后释放锁,但是因为produce不在锁里,所以会出现同时产生一个元素和某个获得锁的消费者判断队列是否为空,因为队列不空,于是该消费者不会wait而是直接consume();而produce之后会唤醒一个等待的消费者来消费,却面临着队列中的已经被消费,队列为空的null指针错误;其主要原因是使用if判断,所以这种wait等待时最好使用while循环判断,就可以避免这种情况!

(4)互测思路

讲道理(可能这个道理说不通),在有限的时间内看七份多线程代码,再考虑一下OS等因素,实在是感觉时间有点不够,而且多线程的理解也相对比较费力,所以没有评测机的我只能选择抱紧大佬大腿,利用评测机测bug感觉也不是很高效(当然OS期中考试也算一个因素),总之感觉互测确实很吃力,有些无从下手的感觉;当然还是尝试了一下,尽管可能第一次和第二次实在是找不出来放了空🔪,不过在第三次还是找出了几个bug的

思路总结:

  1. 我首先选择使用最基础的无脑遍历所有单楼层情况,即只有一条指令,遍历所有情况来检查基础正确性。
  2. 然后就是利用随机生成的数据测试正确性,这里主要是利用大佬的评测机来检查正确性,后来在助教和同学的提醒下,自己也思考了如何写一个Java评测机,但是苦于OS期中放弃了

(5)体会感悟

经过三次作业,对于多线程的感觉就是,没事不要多线程(真香,但是速度好像确实快_

好吧,多线程的学习过程恰好与OS的进程线程相逢,感觉就是安排好了(相约多线程!),但是不可否认,多线程给我的感觉就是,有点神奇——多种意义上!

  1. 这个线程不太稳:多线程的引入对于共用对象互斥访问就有了强制要求,于是需要实现锁机制来保证线程安全,而且在思路上首先得说得过去,不然必然崩掉......

  2. 如何有效的停止:我们都知道有线程安全类,但是深入一看,也是锁,好吧,于是在本次的停止过程中我就不得不自己思考了,因为我发现自己不会直接使用线程安全类停下来,于是还是包装了;

    在搜索学习后,得知了interupt()中断大法,但是还得获得线程对象,有时候还不是很方便!,但是之后发现了ReentrantLock,使用起来方便多了(就是有点迟)

  3. 其实上周五上机的时候才发现,把锁当成PV操作使用,OS和OO神奇的联合起来了,而且还挺好用!!Σ(っ °Д °;)っ进一步想到了管程,但是好像在电梯上使用这个不太方便,反正自己是不知道如何实现,或者说,线程安全队列就是一个类似于管程的东西??

  4. 多线程最重要的还是线程安全,否则优化就是搞事情,强行把自己玩没了(5行修复一个bug,emmm,对于某些神奇之处可能不够,)而且最神奇的是一个bug的出现概率,因为多线程的随机性,有可能会出现bug 出现的概率较低,这时评测机大多是测不到的(我个人感觉,助教说对于某个案例会有多个测试点,但是我自己都出现了交两次就过了的情况,实在是不知其中缘由!😭)

好吧,多线程的学习应该才刚刚开始,如何做到有效高并发并且代码比较合理还有一段路走,但是个人的进步也是有的,我感觉OO学习过程还是痛并快乐着的,毕竟我开着出租车等电梯上楼求导呢!

posted @ 2019-04-23 23:27  小猫爱吃鱼meow~  阅读(201)  评论(0编辑  收藏  举报