面对对象第二单元总结 - 电梯(java多线程)

概述

刚刚过去的oo第二单元主要是来训练我们java多线程设计。

借助电梯这个载体,逐步深入,线程的信息交互,控制也随之复杂。

尽管,我本单元成绩不是很好看,但是,确实学到了不少东西。

下面,我就借助分析三次作业,来谈下我的收获。

作业分析

第一次作业

(1)任务分析

本次作业需要模拟一个多线程实时电梯系统,从标准输入中输入请求信息,程序进行接收和处理,模拟电梯运行,将必要的运行信息通过输出接口进行输出。

本次作业电梯系统具有的功能为:上下行,开关门,每运行一层的时间为固定值,开关门的时间也为固定值。

电梯系统可以采用任意的调度策略,即上行还是下行,是否在某层开关门,都可自定义,只要保证在系统限制时间内将所有的乘客送至目的地即可。

电梯系统在某一层开关门时间内可以上下乘客,开关门的边界时间都可以上下乘客

至于调度算法-简单FAFS(傻瓜调度即可)

(2)设计策略

请求模拟器作为一个线程,负责接受在线请求并负责将请求放入请求队列中。

电梯作为一个线程,从请求队列get到所需请求并模拟电梯行为

请求队列是请求模拟器和电梯线程的共享对象,负责完成线程通信,控制。

其实,第一次作业比较简单,只需要考虑以下几个问题

问题1 电梯如何完成请求

先接到请求发出者,然后完成该请求。在第一次作业中,我们无需判断电梯运动方向,所以这个问题就比较简单了,只需要sleep盲目模拟即可

问题2 电梯线程什么时候死亡?

请求模拟器没有更多请求产生 & 请求队列等待人数为0 & 电梯中没有人

问题3 线程通信,控制

第一次作业,轮询无碍,至于同步问题,给请求队列的get() put()函数加锁即可

(3) 度量分析

代码规格

 类图

复杂度分析

可以看出来本次作业的工作量不是很大

SOLID原则分析

本单元,因为我没有使用继承和接口,所以在下面的SOLID原则分析中,在下仅仅分析SRP和OCP

首先,我这次严格进行了SRP-输入类只负责输入,电梯只负责运行,调度器只负责存储请求队列。

由于是第一次作业,我暂时未考虑可扩展性

线程协作图(后面俩次作业的线程协作图也是如下,就不多次展现了)

Bug分析

第一次比较简单,侥幸没有中招

第二次作业

(1)任务分析

这次作业较第一次的改动如下

1:需要改变自己的调度方法(指导书推荐为ALS调度)

2:改变楼层限制 -3层 - -1层 和 1层 - 16层,每到一层楼需要输出Arrive at 当前楼层

3:为了便于携带,需要区分主副请求

此外需要注意的是,因为需要输出Arrive信息,所以我们需要设定电梯运行方向

为了满足评测限制,需要将调度算法改为ALS,并尽量使用notify(),wait()完成线程控制

(2)设计策略

(我没有按照指导书的方法区分主副请求,我是按最远的为主请求。)

1:当请求模拟器无法产生更多请求 & 请求队列为空 & 当前主请求完成后,结束当前电梯线程

反之,将成功获取一个主请求后,进入执行主请求的时候,每到一层,看当前楼层是否有可携带的人。

如有,看是否改变当前电梯的需到达的最高楼层和最低楼层。当电梯无人的时候 外加 第一个人接到后,结束本次任务。

2:get()函数如果发现请求队列为空的话,wait()阻塞,等待请求模拟器产生请求的时候,notifyall()通知消费者

 

我这样设计的话,固然性能分可能高点

但是有很大缺点,最严重的一点就是乘客体验可能极其差(强烈谴责),根本不可能用在实际生活中。

(3)度量分析

代码规模

类图

复杂度分析

emmm,可以看出来,我有些操作设计的不是很好,原因主要在于我在Elevator.run()中写了电梯的运行过程

其实,按照老师业务逻辑和行为逻辑分离的思想,我应该单独领出来该部分成为一个函数,来实现业务和逻辑分离

SOLID原则

本次作业和上一次作业基本架构相同,符合单一责任原则(SRP),

在本次作业中我没有考虑多电梯的可拓展性,所以我认为在开放封闭原则(OCP)上做的还不是很好,其他原则在本次作业中也无需考虑。

Bug分析

刚刚提到了第二次作业较第一次作业,我们需要完成电梯运行方向的设置。因为我改变了ALS调度策略,导致电梯中可能存在需要电梯向上和需要电梯向下运行的乘客

但是由于我的方向判断错误(始终以主请求来判断方向),导致电梯最后直接奔向十八层地狱,枯了,还是私下测试过少,导致的问题

第三次作业

(1)任务分析

本次作业,需要完成的任务为多部多线程智能(SS)调度电梯的模拟

较前面几次作业的区别

1:多部电梯

2:载客限制

3:可到楼层限制

4:不同电梯运行速度区别

(2)设计策略

问题1 换乘问题

设置person类 (我认为乘客应该了解自己如何换乘),一开始首先利用课程提供的接口来获取乘客的信息(id,起始楼层,终止楼层)进行检测,看单部电梯是否可以完成该请求,如若不能的话,就按照一定策略,设置该乘客的换乘楼层。

(比如可以固定一楼为换乘站,但是可以进行优化,通过比较乘客的起始楼层,和到达楼层来选择换乘楼层)

等该乘客换乘出电梯的时候,立刻根据其的信息,生成新的请求放入调度器的请求队列

问题2 电梯结束条件

这次的电梯线程终止条件发生了变化,记得加上条件 无运行电梯(为此,我在调度器中增加一个变量flag代表当前daintiness运行数量,当一个电梯当前任务完成时,cnt--并notifyall()(为了完成换乘问题,以防需要换乘的电梯已经wait()))

问题3 如何分配乘客

我依旧采取电梯之前自己来抢请求的策略。

我给每个乘坐者person设置了一个权限数组,代表可以乘坐哪部电梯。每部电梯在携带或者get主请求的时候,仅仅只能拥有当前电梯权限的电梯

所以第三次作业的框架较第二次的框架没有什么大的变化

(3)度量分析

代码规模

 类图

复杂度分析

SOLID原则

本次作业输入线程负责向请求队列输入,电梯线程可能会向请求队列输入和输出,

但是我没有专门设置接口管理输入和输出,在单一责任原则(SRP)上可能做的不是很好。

至于扩展性方面,我设置Typecheck类来完成可达性检查,如果增加电梯数量的话,可以比较轻松的解决

二、互测策略

第二单元的互测难度真心比第一次的大很多,因为一个人写评测机的工作量较大,所以我自己仅仅写了一个分析最后输出结果是否正确的程序

至于如何测试代码,我是分为以下几个策略的

1:测试同时间多位顾客的请求可否完成

2:测试是否可以完成携带工作

3:测试是否可以正常线程结束工作

因为多线程的错误难以复现,有时候自己明明测试出来错误,却发现无人中刀

所以本周互测积极性不是很高

三、收获与感想

尽管,这一单元,我的成绩不是很理想,但是还是学习到了很多多线程知识。

多线程设计第一点就是要保证线程安全!线程安全!线程安全!

第二点就是要选择好的线程通信方法,轮询是不可能的,最起码也要采用wait(),notify()

这个的使用就需要注意,要不然很有可能就是死锁

不过,死锁了也不要过于着急,在wait(),和notify()周围printf一下,可以解决绝大部分问题

(printf大法好)

对了,千万不要忘记一个好的架构!要不然,一次重构一次爽,全部重构火葬场

至于优化,千千万万要保证程序的正确性,否则可能得不偿失(别问我,怎么知道滴!)

猜你喜欢

转载自www.cnblogs.com/MGcyh/p/10760095.html