本单元的题目为设计电梯,通过这单元的学习,我初步了解了关于java多线程编程及线程之间并发安全性设计等方面的内容。以下为对这三次作业的分析与总结。

作业分析

序号

楼层

电梯数量

可停靠楼层

调度策略

1

1-15

1

1-15

任意

2

-3--1,1-16

1

-3--1,1-16

捎带

3

-3--1,1-20

3

A:-3--1,1,15-20

任意

B:-2,-1,1,2,4-15

C:1,3,5,7,9,11,13,15

 表1:三次电梯设计要求

图1.电梯设计——类与结构

一、

  第一次的电梯比较简单,楼层均为正数,一部电梯,且不限制调度策略,更重要的是不计性能分数。因此这次只需构造出读写线程,调度的时候来一个请求就执行一个请求,不需要什么算法即可实现。分析以下电梯问题不难发现这其实是一个经典的消费者——生产者模式。电梯的“按钮”(Producer)负责发出(produce)请求——调度器(Tray)收到了请求——电梯(Consumer)负责执行(consume)请求。套用该模型即可完成此次实验。

二、

  第二次的电梯稍稍麻烦了一些。首先,这次电梯引入了负层,乍一看和正层没什么区别,但在日常生活习惯上没有0层的存在,因此在地上下层切换的时候不能带入0层。其次,第二次电梯对调度策略有了要求,至少效率要达到“捎带”。我认为捎带有三层含义:第一,不能一个一个执行请求,因为捎带的时候电梯中可能有多个人;第二,要实现“捎带”。第三,要有实时的策略判断。此次的电梯仍然沿用第一次作业的生产者和消费者模式。

  负层的引入容易解决,只需要特判1<-->-1的情况,改变输出层数即可。就“捎带”算法而言,对于第一点,电梯中有多个人,显然要存储电梯里面的人的请求信息,因此我设计了一个链表来存储这些数据。

  对于第二点的“捎带”策略,捎带条件很容易理解并实现,关键在于我们需要判断“捎带”。“捎带”的判断即捎带条件,我们已经理解;那么需要在什么时候判断需要“捎带”呢?首先我想到的是来一个请求就判断符不符合判断,即“先验性”判断,但回头一想,根据电梯的实时性,在判断后可能出现其他的请求,过程难以预料且复杂多变,管理这些“先验”判断想想都让人头皮发麻,因此此路不通。而后一想,“捎带”这个动作并不是在任意的时间内发生的,只有在电梯正好到达楼层的情况下才可能出现,因此我们只需要在到达楼层的时机,判断有没有可以稍带的就行了。那么我设计了在电梯到达楼层时进行“捎带”判断,解决了这个问题。

  至于第三点,这里的实时问题的具体表现为如何一次读入所有的同时请求。此次的接口是阻塞性单个读取,显然不能一次读取多个请求,但我在这个问题的解决上花了不少时间。最后才明白,解决是不可能解决的,这辈子都不能解决,接口使然。后来捋清了思路,大概是以下的思考过程。

图2.“捎带失败”

也就是说,当送入了一个新请求后,电梯便开始判断,若发现不能捎带的话,便直接离开该楼层,导致可能会错过“捎带”。因此为了避免这种情况,当电梯有开门需求时,在中间的空闲时间内调度器可以接受指令,到了关门的时刻电梯再次询问一遍,可以保证不会漏掉“捎带”请求。但对于电梯不开门的情况,我到现在还没想出一种更为优雅的方法解决,只能在取指令的时候不断wait(),才能笨拙地解决,同时带来了时间的延迟和负载的增加。

三、

  这一次作业相当惭愧,只过了弱测,没有进入互测环节。这次失败的主要原因是对线程安全理解不到位,交上去之后先是读不到最后一条指令,之后又是CPU超时,结果就是绊倒在了中测路上。

  对于读不到最后一条指令的问题,我分析了我的代码。为了解决如二所述的漏掉“捎带”问题,我设置了一个请求缓冲类,所以说我的发送请求流程变为以下模式。

图3.输入处理

  如图所示,这样处理就会出现读不到和null用时出现的最后一条指令,因为此时缓冲类可能正在检查是否结束,发现有了end,便结束了,但是此时end前的一条指令还未送到buffer中。为了修复该问题,当时只得采用“缓兵之计”,让Button读取后sleep一段时间。在bug修复阶段,我决定不弄这些花里胡哨的东西了,所以我选择删掉这段代码(嗯,一劳永逸)。

  对于CPU超时问题,一开始是死活想不出来,检查了好几遍代码,并没有发现暴力轮询的迹象,改了几个地方,最后还是没有过。后来在BUG修复阶段,冷静下来之后,才发现自己的设计中可能存在隐形的轮询的情况——不同于易发现的循环内的暴力轮询,而是线程不断竞争形成的轮询。以下是我的作业中中发生轮询的代码。

 synchronized boolean getSignal(Elevator e) throws InterruptedException {
while (firstTrip.isEmpty() && e.isEmpty() && !isOver()) {
wait();
}
transfer(e);
notifyAll();
return dispatch(e);
}

当某个电梯获得同步后运行到此处,若不满足等待条件,就会直接运行下面的三个函数;如果该电梯不断获得同步,那么就会不断进入函数进行运算,这就增加了大量的CPU时间,导致出现了CPU超时的情况。分析自己的设计后,发现我并不需要让电梯一直请求调配,在判断是否应该阻塞时,就可以分析出电梯此时有没有任务需求。因此只需在判断条件中加入是否有该电梯需要的任务,没有的话就阻塞,防止函数“空转”,以下为修改后的函数。

 synchronized boolean getSignal(Elevator e) throws InterruptedException {
while (!adjustDir(e)) {
wait();
}
transfer(e);
notifyAll();
return dispatch(e);
}

将判断条件改为有无任务需求,将判断条件精确化,防止判断条件太过于泛化导致即便没有任务需求,也调用了函数,消耗大量CPU时间。

心得体会

  这单元主要训练了对多线程编程的理解。第一次作业目的在于让我们对多线程有个初步的了解(线程的创建、并发情况以及基础、经典的P-C模式),第二次作业在第一次的基础上对多线程的性能做了要求,第三次作业在第一和第二次作业的基础上又增加了同类线程之间的协作和通信的要求。对于多线程的心得与体会,可从线程安全和设计模式两个方面来考虑。

1. 线程安全

  线程安全问题主要针对于临界(共享)资源,即临界资源在一个时间只能由一个线程访问,这很好理解,多个线程访问的话如果进行了不同的操作,那么线程可能得到了错误的数据。对于简单的多线程模式,只需要在访问临界资源时获得同步就能保证线程安全,在这单元里,我的临界资源是调度器,电梯获得调度器的同步后,才能够调用调度器的函数。

  值得一提的是,我在bug修复阶段,电梯访问的一个调度器的函数没有加锁,如下面代码所示:

 Elevator:
public void run() {
...
while (!noTask() || ...) {
...
}
...
} Dispatcher:
boolean noTask() {
...
for (Resource r: rList) {
...
}
...
}

  rList数据的修改只能够由获得了同步的电梯进行修改,想到方法的同步是整个对象,因此后加的noTask函数就没同步,反正访问时肯定是没有其它线程占用,也是独享资源的。结果交上去出现了Java ConcurrentModificationException,for-each发生了异常。查询了关于该异常的部分知识,我发现了问题所在:虽然进入访问的时刻没有其他线程,但在访问的for-each时间内,其他线程可能进入了Dispatcher,修改了rList,从而造成异常。因此,对于多线程的临界资源,一定要进行全面的线程安全考虑。

2. 设计模式

  这个单元作业我采用了单例模式,所有的优点围绕只会实例化一个对象展开,比如这次作业的调度器,只需要一个,可以采用单例模式设计。但是由于个人的水平处于新手阶段,对编程理解并不到位,还是不太理解单例模式的优点,感觉就是实例化一个对象而已,设计工程之前,肯定会提前知道需要几个这样的对象,单例模式就显得很鸡肋。不过用了之后感觉比较省心倒是真的。

  

BUAAOO——UNIT2 SUMMARY的更多相关文章

  1. OO unit2 summary

    Unit2 一.第一次作业 1.UML 2.Sequence Diagram 3.同步块设置与锁处理 采用了生产者-消费者模式,用共享对象来连接不同的线程. 第一次作业中,我有三个线程:Receive ...

  2. Summary of Critical and Exploitable iOS Vulnerabilities in 2016

    Summary of Critical and Exploitable iOS Vulnerabilities in 2016 Author:Min (Spark) Zheng, Cererdlong ...

  3. 三个不常用的HTML元素:<details>、<summary>、<dialog>

    前面的话 HTML5不仅新增了语义型区块级元素及表单类元素,也新增了一些其他的功能性元素,这些元素由于浏览器支持等各种原因,并没有被广泛使用 文档描述 <details>主要用于描述文档或 ...

  4. [LeetCode] Summary Ranges 总结区间

    Given a sorted integer array without duplicates, return the summary of its ranges. For example, give ...

  5. Network Basic Commands Summary

    Network Basic Commands Summary set or modify hostname a)     temporary ways hostname NEW_HOSTNAME, b ...

  6. Summary - SNMP Tutorial

    30.13 Summary Network management protocols allow a manager to monitor and control routers and hosts. ...

  7. Mac Brew Install Nginx Summary

    ==> Downloading https://homebrew.bintray.com/bottles/nginx-1.10.1.el_capitan.bot################# ...

  8. Leetcode: LFU Cache && Summary of various Sets: HashSet, TreeSet, LinkedHashSet

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  9. How to add taxonomy element to a summary view?

    [re: Orchard CMS] This caused me scratching my head for days and now I can even feel it's bleeding. ...

随机推荐

  1. mac命令日常总结

    查看某个端口被占用 lsof -i tcp:8080 kill进程: 找到进程的PID,使用kill命令:kill -9 716(PID) date 显示系统日期 mkdir xx 创建xx目录 rm ...

  2. 微信发送朋友圈URL JSAPI事件demo

    <script> var imgUrl = 'http://m.ximiyu.com/content/images/thumbs/0000126_540.jpeg'; var lineLi ...

  3. Nginx笔记总结十一:Nginx重写规则指南

    依赖PCRE库,需要安装pcre,最多循环10次,超过后返回500错误, 1.       rewrite模块指令 break:完成当前设置的重写规则,停止执行其他的重写规则 if:  if () { ...

  4. 4.2英寸的iPhone SE2就要来了!但你还会买单吗?

    SE2就要来了!但你还会买单吗?" title="4.2英寸的iPhone SE2就要来了!但你还会买单吗?"> 与其他手机厂商不同,苹果在手机市场的产品策略很&q ...

  5. struts2和springmvc性能比较2

    我们用struts2时采用的传统的配置文件的方式,并没有使用传说中的0配置.spring3 mvc可以认为已经100%零配置了(除了配置spring mvc-servlet.xml外). Spring ...

  6. 「CometOJ」Contest #11

    Link Aeon 显然字典序最大就是把最小的字母放在最后 Business [动态规划] 简单dp dp[i][j]dp[i][j]dp[i][j]表示到第iii天,当前有jjj块钱,最后返还的钱最 ...

  7. Python 代码实现验证码识别

    Python 代码实现验证码识别 测试开发社区  1周前 源 /  j_hao104 一.探讨 识别图形验证码可以说是做爬虫的必修课,涉及到计算机图形学,机器学习,机器视觉,人工智能等等高深领域…… ...

  8. 如何安装与配置MySQL

    关键词:MySQL,安装,配置 这一节,我们讨论一下MySQL的安装配置与卸载 下载 网址:https://dev.mysql.com/downloads/mysql/ 选择社区版,找到对应的电脑,开 ...

  9. Autowired和Resource区别

    @Autowired和@Resource熟悉吧?是不是经常复制粘贴顺手就来,两者都是用来给成员变量自动装载,可是它俩到底有啥区别呢? 1.@Autowired与@Resource都可以用来装配bean ...

  10. nginx图片过滤处理模块http_image_filter_module安装配置

    http_image_filter_module是nginx提供的集成图片处理模块,支持nginx-0.7.54以后的版本,在网站访问量不是很高磁盘有限不想生成多余的图片文件的前提下可,就可以用它实时 ...