面向对象之多线程(可捎带电梯调度)

1. 题目重述

​本题完成的任务为多部多线程可捎带调度电梯的模拟,电梯系统具有的功能为:上下行、开关门、新增一部可以使用的电梯,电梯系统在某一层开关门时间内可以上下乘客。电梯系统可以采用任一的调度策略,只要保证在一定时间内将所有乘客送至目的地即可。

​本题采用的是目的选层电梯,在电梯的每层入口,都有一个输入装置,让每个乘客输入自己的目的楼层,所以一个电梯请求除了人员id,还有这个人的出发楼层和目的楼层。

​电梯类型分为A型、B型、C型,在新增电梯时指定类型。初始有3部电梯,编号分别为A、B、C,新增电梯编号为X1、X2、……、Xn。三类电梯的开关门时间都分别为0.2s。

三类电梯的可停靠楼层、上升或下降一层时间、最大载客量

A型:-3, -2, -1, 1, 15~20 0.4s 6人

B型:-2, -1, 1, 2, 4~15 0.5s 7人

C型: 1, 3, 5, 7, 9, 11, 13, 15 0.6s 8人

样例输入

[1.0]X1-ADD-ELEVATOR-A

[2.0]1-FROM--3-TO-2

样例输出

[2.4060]ARRIVE--1-X1

[2.8070]ARRIVE--2-X1

[3.2070]ARRIVE--3-X1

[3.2080]OPEN--3-X1

[3.2080]IN-1--3-X1

[3.6080]CLOSE--3-X1

[4.0090]ARRIVE--2-X1

[4.4100]ARRIVE--1-X1

[4.8100]ARRIVE-1-X1

[4.8100]OPEN-1-X1

[4.8100]OPEN-1-B

[4.8110]OUT-1-1-X1

[4.8110]IN-1-1-B

[5.2120]CLOSE-1-B

[5.2120]CLOSE-1-X1

[5.7120]ARRIVE-2-B

[5.7120]OPEN-2-B

[5.7130]OUT-1-2-B

[6.1130]CLOSE-2-B

2. 电梯调度策略

2.1 ALS调度策略

ALS电梯的请求分为主请求和被捎带请求

主请求选择规则:

如果电梯中没有乘客,将请求队列中到达时间最早的请求作为主请求

如果电梯中有乘客,将其中到达时间最早的乘客请求作为主请求

被捎带请求选择规则:

电梯的主请求存在,即主请求到该请求进入电梯时尚未完成

该请求到达请求队列的时间小于等于电梯到达该请求出发楼层关门的截止时间

电梯的运行方向和该请求的目标方向一致。即电梯主请求的目标楼层和被捎带请求的目标楼

层,两者在当前楼层的同一侧。

其他:

标准ALS电梯不会连续开关门。即开门关门一次之后,如果请求队列中还有请求,不能立即

再执行开关门操作,会先执行请求。

2.2 LOOK调度策略

2.2.1 SCAN调度策略的改进

​SCAN调度策略,使电梯沿一个方向进行扫描,每到达一个楼层,捎带当前楼层本电梯可以捎带(该请求的到达楼层是本电梯可停靠的楼层且电梯此时尚未满载)的请求,直至电梯到达运动方向上最远的可停靠层,再转变运动方向进行扫描。简单来说,理解为电梯在最低和最高可停靠层来回扫描,同时捎带和完成请求。

​很明显,这样的调度策略很有可能会造成性能的损失,如果运动方向上没有需要捎带的请求或者电梯已无法捎带请求并且目前已在电梯内的请求需要停靠的楼层都在相反方向,则可以立刻调转方向扫描,减少来回的无效路程,这就是LOOK调度策略,具体的电梯线程实现可见下方的流程图。(有人可能会说,万一调转方向后,原先的运动方向上产生了需要捎带的请求呢?本人的观点是,这种情况的确有可能出现,但是电梯无法预知未来,所以只能选择当前情况下的最优选择,在大部分情况中,采用LOOK调度的性能要比SCAN调度好)

2.2.2 电梯线程流程图

2.2.3 换乘策略

​由于本次题目的请求并不一定可以由一个电梯单独完成,所以需要让乘客进行换乘。

​具体的实现方式是:在PersonRequest类的原有属性fromFloor和toFloor上增加finalFloor属性,如果该请求不需要换乘,将finalFloor赋值为0,否则将finalFloor赋值为toFloor,将toFloor赋值为中转楼层。当该请求到达中转楼层toFloor时,向调度器的请求队列中增加一个新请求,新请求的fromFloor为中转楼层、toFloor为原先的finalFloor(即该请求所要到达的最终楼层)、finalFloor为0。具体的换乘策略如下表所示:

fromFloor

toFloor

中转楼层

-3

2~14

1

-2、-1、2

3

1

2~14

-3

1

3

-2、-1、2

1

3

4、6、8、10、12、14

5

4、6~14

3

5

16~20

2~14

15

2~14

16~20

15

3. 多线程交互模式

3.1 生产者-消费者模式

3.1.1 模式定义

​生产者(Producer)是负责产生数据的线程,消费者(Consumer)是处理数据的线程,当生产者消费者以不同的线程运行时,两者之间的速度差异会引起问题,比如消费者想要获取数据,数据还未生成;生产者想要交付数据,消费者还无法接收数据。为了解决这种生产者和消费者的强耦合问题,可以让生产者消费者通过缓冲区(Channel)进行通讯,进而消除不同线程间处理速度的差异。

​通俗来说,生产者将生产的数据直接移交缓冲区,消费者直接从缓冲区中提取需要的数据,两者不直接交互。

3.1.2 模式结构

3.1.3 本题UML时序图

3.2 观察者模式

​观察者模式定义了对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新,最典型的例子就是微信公众号订阅与通知系统。

​微信公众号是被观察者,每一个关注公众号的用户是观察者。一旦公众号有更新(被观察者状态改变),就会向每一个关注该公众号的用户推送信息(通知观察者),然后关注此公众号的用户在微信中看到新信息(观察者状态更新)。

在观察者模式中通常会先设计观察者与被观察者的接口

观察者接口中包含update方法,用于被观察者状态更新后对观察者进行通知。

被观察者接口中包含增加观察者、移除观察者以及通知其所有观察者的方法。

设计观察者类和被观察者类实现对应的接口

在被观察者类中,需要维护一个列表用来存放其所有观察者。

通知方法通过对其所有观察者调用update方法实现通知与观察者的更新。

4. 第一次作业的血泪史

4.1 使用wait()避免暴力轮询

问题原因:消费者线程使用暴力轮询来观察是否有新的请求产生,导致CPU运行超时

惨况:在强测AC的情况下,互测中因为CPU运行超时被hack了19刀

解决方法:当请求队列中没有可以接收的请求时,使用wait()使消费者线程进入等待状态,等待notifyAll()指令唤醒线程。其实,多线程协调运行的原则就是,当条件不满足时,线程进入等待状态;当条件满足时,线程被唤醒,继续执行任务。

4.2 不是所有的同步方法都需要notifyAll()

问题原因:对于多线程同步方法的机制不清楚,在每个加了synchronized的方法中都notifyAll()

解决方法:了解多线程中线程同步的实现机制,以对象锁的同步方法为例

public synchronized void method() { // 锁住this

​……

} // 释放锁

​用synchronized修饰的方法是同步方法,锁住的对象是this,也就是调用该方法的实例,在生产者-消费者模式中,锁住的通常就是共享对象。

锁池(lock pool):假设线程A已经拥有了对象object的锁(正在调用object的某个同步方法),而线程B也试图获取object的锁(试图调用object的同步方法),但是object的锁已经被A占有,线程B就会被阻塞,进入object的锁池中等待。当线程A(执行完同步方法或执行object的wait())释放锁时,位于object锁池中的线程去竞争这把锁,竞争成功的线程则获得这把锁,竞争失败的线程则继续被阻塞在锁池中。

等待池(waiting pool):线程A调用了对象object的wait()时,线程A会释放object的锁同时A就进入object的等待池等待,当锁被释放时,等待池中的线程不会去竞争该对象的锁。当object的notifyAll()被调用后,会唤醒所有等待池中的线程,被唤醒的线程进入object的锁池中,这些线程就可以去竞争object的锁了。

​在对锁池和等待池有一个清晰的了解下,可知绝大多数情况下,可以理解为该同步方法可能改变其他等待池中线程的状态(使其不再等待)时,才需要在保证线程安全的地方加上notifyAll()唤醒等待池中的线程。

5. 面向对象程序设计原则

以下内容引自课件“面向对象程序的需求分析与设计原则-1-分析与设计原则”

5.1 SOLID(经典的5个设计原则)

5.1.1 SRP(Single Responsibility Principle)——单一职责原则

​SRP原则,顾名思义,就是每个类或方法都只有一个明确的职责,类职责就是使用多个方法从多个方面来综合维护对象所管理的数据,方法职责就是从某个特定方面来维护对象的状态(更新、查询等)。

​如果类或方法的职责过多,会导致类或方法的逻辑难以封闭,容易受到外部的影响,因外部因素的变化而变化。

5.1.2 OCP(Open Close Principle)——开闭原则

​OCP原则的含义是对扩展开放,对修改关闭。简单来说,当程序进行扩展增加新功能(open)时,不需要修改原有的代码(close)。OCP原则能够使程序的扩展性更好,易于维护和升级。

​在使用继承实现OCP原则的过程中,需要保持好子类和父类之间的交互关系,并使用好接口和抽象类。

5.1.3 LSP(Liskov Substituion Principle)——里氏代换原则

​LSP原则的含义是任何父类出现的地方都可以用子类来代替,并且不会导致相应类的程序出现错误。实现OCP原则的关键步骤就是抽象化,而父类与子类的继承关系就是抽象化的具体实现,所以LSP原则是对实现抽象化的具体步骤的规范。

反例:子类虽然继承了父类的属性和方法,但子类也增加了一些新的属性和方法,可能会破坏父类存在的某些约束。比如Queue类和SortedQueue类,Queue类提供了一个返回最近一次入队列的元素,其实现方式是返回队列尾部的元素,但是SortedQueue类,会对队列中的元素进行一个排序,返回队列尾部的元素无法实现返回最近一次入队列的元素。

5.1.4 ISP(Interface Segregation Principle)——接口隔离原则

​ISP原则的含义是一个接口只封装一组高度内聚的操作,使用多个隔离的接口,避免封装多种可选的方案,降低类之间的耦合度。

5.1.5 DIP(Dependency Inversion Principle)——依赖倒置原则

​DIP原则的含义是高层次模块不应该依赖于低层次模块,应该依赖于抽象,针对接口编程。

5.2 其他重要设计原则

层次化抽象原则:程序中具有抽象层次关系(继承或实现接口等)的类在问题域中也具有同样的抽象层次关系。

均衡原则:避免出现God类和Idiot类。God类就是”无所不能“的类,各种事情都在这个类中完成,会使这个类十分臃肿;Idiot类就是”什么也干不了“,只有一两个属性和get、set方法的类。

局部化原则:类之间不要冗余存储相同的数据,方法之间不能够出现控制耦合。当类C获得了数据D后,其他类如果想获得数据D,只能向类C要。控制耦合就是直接根据传递进来的参数来决定是否要执行某个分支。

完整性原则:一个类需要提供针对相应数据进行处理的完整方法集(完整是相对于问题域需求的)。

共性抽取原则:把不同类之间具有的共性数据或处理抽象成层次关系,避免冗余。

显示表达原则:显示表达所有想要表达的数据或逻辑,不适用数组存储位置或者常量来隐含表示某个特定状态或数据。因为维护程序的人有可能会get不到这里的隐含逻辑。

信任原则:调用方法时,调用者需要检查和确保方法的基本要求能够被满足,获得调用结果后需要按照约定的多种情况分别进行处理。

懂我原则:所有类、对象、变量、方法等的命名做到“顾名思义”。不要嫌变量或方法名长(当然也不能太长),维护程序的时候一旦理解错了变量或方法的含义,得不偿失。

Tips:在许多项目中,都会使用一些约定俗成的缩写方式,比如"4"--"for","2"--"to"等,尽量避免使用只有自己看得懂的缩写。

具体的命名规范可以参考《阿里巴巴Java开发手册》。

java多线程电梯调度_面向对象之多线程(可捎带电梯调度)相关推荐

  1. java死锁业务场景_【深入浅出多线程系列十二】:什么是死锁?(场景+代码示例)...

    在学习Java的道路上,是否路过多线程时总让你很迷惘:很不巧,我也是,而使我们感到很迷惘主要原因都源于没有对概念的深深的理解和实践.所以我决定漫步Java多线程,同你一起会会多线程. 多线程系列 多线 ...

  2. java 线程的构造函数_[c++11]多线程编程(二)——理解线程类的构造函数

    构造函数的参数 std::thread类的构造函数是使用可变参数模板实现的,也就是说,可以传递任意个参数,第一个参数是线程的入口函数,而后面的若干个参数是该函数的参数. 第一参数的类型并不是c语言中的 ...

  3. python 电梯运行_面向对象电梯系列总结

    一. 设计策略 1. 架构设计 三个线程:电梯,调度器,主线程(输入线程), 采用worker thread,生产者消费者模式.和同学讨论,发现有的观点认为:调度器更像是一个功能的集合,类似一个函数, ...

  4. qt udp多线程收发过程_! udp多线程的有关问题

    求助! udp多线程的问题 我做了个udp接收的测试程序,刚开始能收到数据,收了几条数据之后就报个错: QSocketNotifier: Socket notifiers cannot be enab ...

  5. java程序设计清考_面向对象程序设计(Java)-题库

    <面向对象程序设计(Java)-题库>由会员分享,可在线阅读,更多相关<面向对象程序设计(Java)-题库(33页珍藏版)>请在金锄头文库上搜索. 1.面向对象程序设计 (ja ...

  6. java class 生成对象_面向对象编程,你知道Java有哪些创建对象的方式吗?

    来源:CSDN____爱敲代码的小游子 1.用new关键字创建对象,需要使用构造器. new 一个对象经过了哪些过程? 2.使用反射机制创建对象,用Class类或Constructor类的newIns ...

  7. java实现鸭子类型_面向对象—多态、鸭子类型(Day21)

    编程原则java具有自己的编程原则和设计模式,不能多继承. python的编程原则: 1.开放封闭原则:开放是对扩展是开放的,封闭是对修改是封闭的(已经写完的代码程序是不能修改的). 2.依赖倒置原则 ...

  8. java程序设计图形题_面向对象与Java程序设计基础题目:设计一个程序可以一计算平面图形的面积和立体图形的体积。1.使用interface关键...

    共回答了15个问题采纳率:93.3% 1.shape接口: public interface Shape { double getArea(); } 2.shape2D接口: public inter ...

  9. python多线程tcp客户端_基于Python多线程的TCP客户端/服务端应用示例

    每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接. 服务端:server.py # -*- coding:utf-8 -*- import sys i ...

最新文章

  1. 【ubuntu】vim中鼠标选中时变成 可视模式,不能复制的解决方法
  2. 打印机打印一次出三张_一次性帮你搞定3D打印机堵塞问题
  3. 更换紧凑型荧光灯管过程
  4. MySQL中文参考手册-- 常用查询的例子
  5. 【机器学习算法-python实现】扫黄神器-朴素贝叶斯分类器的实现
  6. 中国计算机学会CCF推荐国际学术会议和期刊目录-计算机体系结构/并行与分布计算/存储系统
  7. 计算机应用基础电子毕业考试,计算机应用基础-上海电子信息职业技术学院.PDF...
  8. 华为手机可以下载鸿蒙系统吗_华为鸿蒙手机系统首次曝光,汉字界面简洁明了...
  9. Codeforces - tag::data structures 大合集 [占坑 25 / 0x3f3f3f3f]
  10. JavaFX官方教程(七)之使用FXML创建用户界面
  11. [00011]-[2015-08-26]-[00]-[Windows 程序设计 ---MFC 截屏---BMP格式 ---JPG格式]
  12. 计算机的创新产品,这几种电脑创新功能 空有喝彩没人买
  13. 计算机相关英语词汇ppt,计算机专业英语词汇新.ppt
  14. Learning Premiere Elements 15 Premiere Elements 15教程 Lynda课程中文字幕
  15. php个人财务管理,PHP个人理财管理系统的设计与实现
  16. 写在《大国崛起》之后,“中国崛起”之前(二)
  17. 沈阳学计算机Excel,沈阳excel培训点
  18. ubuntu 20.04 自动重启网络
  19. Kong静态资源配置(直接访问kong返回静态资源)
  20. Incorrect string value: ‘‘\\xE5\\x87\\xBA\\xE4\\xBB\\xBB...‘‘ for column ‘‘note_describe‘‘ at row 1

热门文章

  1. 微信小程序(3)—— 添加联系人至通讯录
  2. 想提前躺平的程序员,这10个网站收好了!
  3. 【codeforces 821E】Okabe and El Psy Kongroo
  4. 扒站,仿站,下载网页,工具,软件,神器,无脑傻瓜版,一键操作
  5. 如何在R中使用floor()和ceiling()函数
  6. r中将数据框中数据类型转化_R中的数据类型
  7. 如何批量生成食品商品条码
  8. MATLAB中的*(乘)和.*(点乘)傻傻分不清?!
  9. Go实现 Bit 数组(集合)
  10. 【计算机网络】家用路由器的NAT模式和路由模式