面向对象第二次作业总结-电梯调度

OO第二次作业总结-电梯调度

 

一.作业回顾

  • 第一次作业:支持FCFS一台电梯调度

  • 第二次作业:支持ALS一台电梯调度

  • 第三次作业:支持协同调度多台电梯调度

 

二.第一次作业

2.1 设计思路

 

 

  本次作业采用三线程,即输入线程,调度线程,电梯线程。每两个线程之间使用一个队列容器作为托盘,实际上是两个生产者-消费者模式,该架构思路即为电梯只负责抓取指令并且运行,而调度器负责给电梯增添指令。

2.2 UML类图与线程运行逻辑与时序图

 

 

  在本次作业中我的主线程只负责创建线程与实例化对象,只是一个操作线程,而没有实际作用,不予介绍。

  • Input线程

    运行逻辑:不停从系统输入中获取输入,并且addqueueForInput中去。

    死亡条件:当输入为null时结束。

     1 public void run() {
     2         //TimableOutput.println("Running" + threadName);
     3         ElevatorInput elevatorInput = new ElevatorInput(System.in);
     4         while (true) {
     5             PersonRequest request = elevatorInput.nextPersonRequest();
     6             if (request == null) {
     7                 //down = false;
     8                 break;
     9             } else {
    10                 //TimableOutput.println(
    11                 // "add " + request.toString() + "to Input queue");
    12                 queueOfInput.add(request);
    13             }
    14         }
    15         try {
    16             elevatorInput.close();
    17         }
    18         catch (Exception e) {
    19             TimableOutput.println("Fucked " + threadName);
    20         }
    21     }
    code run()
  • Scheduler线程

    运行逻辑:不断地从queueOfInputpoll(其中的wait()写在了poll函数里),并且addqueueOfElevator中去。

    死亡条件:当input线程已结束,并且queueOfInput为空。

     1     public void run() {
     2         //TimableOutput.println("Running" + threadName);
     3         PersonRequest request;
     4         while (input.isDown() || queueOfInput.getQueueSize() != 0) {
     5             if (queueOfInput.getQueueSize() != 0) {
     6                 synchronized (this) {
     7                     request = queueOfInput.poll();
     8                     queueOfElevator.add(request);
     9                 }
    10             }
    11         }
    12     }
    code run()
  • Elevator线程

    运行逻辑:不断地从queueOfElevatorpoll(其中的wait()写在了poll函数里),并且直接对poll到的指令进行操作。

    死亡条件:当input线程与scheduler线程已结束,并且queueOfInputqueueOfElevator为空。

     1     public void run() {
     2         //TimableOutput.println("Running " + threadName);
     3         PersonRequest request;
     4         while (scheduler.isOK()) {
     5             if (queueOfElevator.getQueueSize() != 0) {
     6                 request = queueOfElevator.poll();
     7                 getTo(request.getFromFloor());
     8                 openDoor(request.getFromFloor());
     9                 peopleIn(request.getPersonId(),request.getFromFloor());
    10                 closeDoor(request.getFromFloor());
    11                 getTo(request.getToFloor());
    12                 openDoor(request.getToFloor());
    13                 peopleOut(request.getPersonId(),request.getToFloor());
    14                 closeDoor(request.getToFloor());
    15             }
    16         }
    17     }
    code run()
时序图:

 

时序图可以看出三个线程都没有wait(),而是在轮询,输入线程在结束之前告知调度线程,调度线程在结束之前告知电梯线程。

2.3 同步控制实现

  由于是一个简单的生产者消费者模型,故而只需要在容器类的polladd函数中实现同步控制即可。但是本次的代码并没有让线程进入到wait()函数,实际上是一个轮询式的操作。

  本次同步控制的实现一大重点时对于线程运行的控制,用我简单的话来说就是让线程在不应该结束时绝不结束,在线程应该结束时一定结束。其本质是对线程结束控制的信号进行管理。

  比如input线程结束是使用了输入为null的信号。

  scheduler线程结束是使用了input线程已结束,并且queueOfInput为空为信号。

  elevator线程比较有意思,最初我定的结束条件为输入线程结束,并且两个队列为空,当时我认为此条件等同于当前以及未来都没有可处理任务。但是这个等同不是双向的,有一种状况,当输入线程结束,一条指令从queueOfInput被调度线程poll出来,还没有addqueueOfElevator中时,满足该条件,电梯线程结束,有一条指令未处理。而在这个叙述中很容易看出需要将shceduler线程的存活信号加入到判断条件中去。

  从本次的同步控制实现中,我明白线程间的控制,尤其时线程结束的控制一定要结合线程的运行过程,对运行过程仔细思考来决定其结束条件,否则将会出现线程在出乎意料的时刻结束的痛苦(指debug)情况。

2.4 度量分析

类表:
Project NamePackage NameType NameNOFNOPFNOMNOPMLOCWMCNCDITLCOMFANINFANOUT
homework_5elevatorElevator5082711100012
homework_5elevatorInput304336700021
homework_5elevatorMainClass001115100-105
homework_5elevatorQueueFor206540720000
homework_5elevatorQueueOfElevator00329301-130
homework_5elevatorQueueOfInput00105101-130
homework_5elevatorScheduler504333700023

  其中NOF (Number Of Fields)代表域个数,NOM (Number Of Methods)代表方法个数,NOPM (Number of Public Methods)代表公共方法个数,LOC (Lines of Code)代表类总代码规模(行数)。

方法表:
Project NamePackage NameType NameMethodNameLOCCCPC
homework_5elevatorElevatorElevator613
homework_5elevatorElevatorElevator1630
homework_5elevatorElevatorElevator1211
homework_5elevatorElevatorElevator911
homework_5elevatorElevatorElevator911
homework_5elevatorElevatorElevator312
homework_5elevatorElevatorElevator312
homework_5elevatorElevatorElevator620
homework_5elevatorInputInput412
homework_5elevatorInputInput310
homework_5elevatorInputInput1830
homework_5elevatorInputInput620
homework_5elevatorMainClassMainClass1311
homework_5elevatorQueueForQueueFor310
homework_5elevatorQueueForQueueFor811
homework_5elevatorQueueForQueueFor1820
homework_5elevatorQueueForQueueFor310
homework_5elevatorQueueForQueueFor211
homework_5elevatorQueueForQueueFor211
homework_5elevatorQueueOfElevatorQueueOfElevator310
homework_5elevatorQueueOfElevatorQueueOfElevator211
homework_5elevatorQueueOfElevatorQueueOfElevator211
homework_5elevatorQueueOfInputQueueOfInput310
homework_5elevatorSchedulerScheduler614
homework_5elevatorSchedulerScheduler1130
homework_5elevatorSchedulerScheduler310
homework_5elevatorSchedulerScheduler620

  分析程序有一些bug无法看到方法名,但是每个类的长度都不长。

度量分析表:
Methodev(G)iv(G)v(G)
elevator.Elevator.Elevator(String,Scheduler,QueueOfElevator)111
elevator.Elevator.closeDoor(int)122
elevator.Elevator.getTo(int)122
elevator.Elevator.openDoor(int)122
elevator.Elevator.peopleIn(int,int)111
elevator.Elevator.peopleOut(int,int)111
elevator.Elevator.run()133
elevator.Elevator.start()122
elevator.Input.Input(String,QueueOfInput)111
elevator.Input.isDown()111
elevator.Input.run()344
elevator.Input.start()122
elevator.MainClass.main(String[])111
elevator.QueueFor.QueueFor()111
elevator.QueueFor.add(PersonRequest)111
elevator.QueueFor.getQueueSize()111
elevator.QueueFor.poll()133
elevator.QueueFor.printAdd(PersonRequest)111
elevator.QueueFor.printPoll(PersonRequest)111
elevator.QueueOfElevator.QueueOfElevator()111
elevator.QueueOfElevator.printAdd(PersonRequest)111
elevator.QueueOfElevator.printPoll(PersonRequest)111
elevator.QueueOfInput.QueueOfInput()111
elevator.Scheduler.Scheduler(String,QueueOfInput,QueueOfElevator,Input)111
elevator.Scheduler.isOK()144
elevator.Scheduler.run()144
elevator.Scheduler.start()122
    
ClassOCavgWMC 
elevator.Elevator1.3811 
elevator.Input1.757 
elevator.MainClass11 
elevator.QueueFor1.177 
elevator.QueueOfElevator13 
elevator.QueueOfInput11 
elevator.Scheduler1.757 
    
Packagev(G)avgv(G)tot 
elevator1.746 
    
Modulev(G)avgv(G)tot 
homework_51.746 
    
Projectv(G)avgv(G)tot 
project1.746 

  从表中可以看到本次作业的基本复杂度,模块设计复杂度和求导复杂度都非常正常,说明代码的模块设计以及类的功能设计划分,方法的功能设计划分合理,基本符合高内聚低耦合的设计要求。

2.5 程序评价

  不只是这一次,后续的两次作业由于对多线程的恐惧,都没有实现指导书中说的调度线程与电梯线程之间的高强度交互,意即彻底将电梯与调度器的功能分解开,电梯真正的只负责运行调度器分发的指令,而调度器真正掌控全部的调度能力。

  并且本次作业没有使用wait()与notifyAll(),虽然实现了但是没有使用。即三个线程都采用轮询,非常消耗CPU资源。

  另外有一点不知道是否是优点或是缺点,即将线程之间的通信基本通过容器来进行,这样可以让线程之间彼此降低干扰,降低线程运行控制难度,但是增加了通信的代价,即将两个线程之间联通了另一个类来传送信息,同时由于这样不能很全面的获知另一个类中的信息,比如调度器无法获知电梯现在的状态,从而无法进行精准调度。

  但其实本次作业还是对其做出了尝试,比如传递了线程的死亡信号,但是对于高频率的交互,仍然是望而却步了。

  对程序进行SOLID 原则分析

  SRP:每个类的职责都很明确

  OCP:不满足,在本次作业中很多地方都使用了硬编码来简化代码结构,除了整体架构之外很多地方的扩展性较差

  LSP:满足,在出现父类的时候,都可以用子类进行替换。

  ISP:未使用接口

  DIP:符合,高层次模块依赖于依赖于低层次的抽象。

  总体来说这是一次较成功的作业。

三.第二次作业

3.1 设计思路

 

  本次作业仍然采用三线程,即输入线程,调度线程,电梯线程。每两个线程之间使用一个队列容器作为托盘,但是本次将后一个容器改为了hashMap,使用楼层作为key,使用以指令为元素的arrayList作为value,为电梯的捎带操作提供便利,本次作业调度器线程负责将指令塞到等待楼层并根据其上下行,塞到上行或者下行的hashMap中,而电梯则在其为空时寻找一个最近的指令作为主指令,接到主指令之后以主指令运行方向,捎带过程中遇到的同向乘客,直到电梯为空,寻找下一个主指令。

3.2 UML类图与线程运行逻辑与时序图

 

  在本次作业中我的主线程只负责创建线程与实例化对象,只是一个操作线程,而没有实际作用,不予介绍。

  • Input线程

    运行逻辑:不停从系统输入中获取输入,并且addqueueForInput中去。

    死亡条件:当输入为null时结束。

     1 public void run() {
     2         //TimableOutput.println("Running" + threadName);
     3         ElevatorInput elevatorInput = new ElevatorInput(System.in);
     4         while (true) {
     5             PersonRequest request = elevatorInput.nextPersonRequest();
     6             if (request == null) {
     7                 //down = false;
     8                 break;
     9             } else {
    10                 //TimableOutput.println(
    11                 // "add " + request.toString() + "to Input queue");
    12                 queueOfInput.add(request);
    13             }
    14         }
    15         //TimableOutput.println("Fucked " + threadName);
    16         queueOfInput.awakeAndGoDie();
    17         try {
    18             elevatorInput.close();
    19         }
    20         catch (Exception e) {
    21             TimableOutput.println("Fucked " + threadName);
    22         }
    23     }
    code run()
  • Scheduler线程

    运行逻辑:不断地从queueOfInputpoll(其中的wait()写在了poll函数里),并且根据其时上行还是下行addhashMapOfElevator中上行hashMap还是下行hashMap中去。

    死亡条件:当input线程已结束,并且queueOfInput为空。

     1     public void run() {
     2         //TimableOutput.println("Running" + threadName);
     3         PersonRequest request;
     4         while (input.isDown() || queueOfInput.getQueueSize() != 0) {
     5             //TimableOutput.println("sc not die");
     6             request = queueOfInput.poll();
     7             if (request == null) {
     8                 break;
     9             }
    10             if (request.getFromFloor() < request.getToFloor()) {
    11                 hashMapOfElevator.addUp(request);
    12             } else {
    13                 hashMapOfElevator.addDown(request);
    14             }
    15         }
    16         //TimableOutput.println("scheduler died");
    17         hashMapOfElevator.awake();
    18     }
    code run()
  • Elevator线程

    运行逻辑:在其为空时寻找一个最近楼层的指令作为主指令,接到主指令之后以主指令运行方向,过程中取出同层的同向等待指令,直到电梯为空,寻找下一个主指令。

    死亡条件:当input线程与scheduler线程已结束,并且queueOfInputhashMapOfElevator为空。

     1   public synchronized void run() {
     2         //TimableOutput.println("Running " + threadName);
     3         PersonRequest request;
     4         while (scheduler.isOK()) {
     5             //TimableOutput.println("elevator waiting");
     6             queueOfElevator.sleepWaiting();
     7             //TimableOutput.println("elevator awake");
     8             if (queueOfElevator.getDieAndAway()
     9                     && queueOfElevator.getHashSizeDown() == 0
    10                     && queueOfElevator.getHashSizeUp() == 0) {
    11                 break;
    12             }
    13             find();
    14             while (peopleInElevator.size() == 0) {
    15                 ArrayList<PersonRequest> tempArrayList;
    16                 if (inUpOrDown) {
    17                     tempArrayList = queueOfElevator.pollUp(floorNow);
    18                     if (tempArrayList == null) {
    19                         upOrDown();
    20                         find();
    21                         continue;
    22                     }
    23                     else {
    24                         openDoor(floorNow);
    25                         for (int i = 0;i < tempArrayList.size();i++) {
    26                             peopleInElevator.add(tempArrayList.get(i));
    27                             peopleIn(tempArrayList.get(i).getPersonId(),
    28                                     tempArrayList.get(i).getFromFloor());
    29                         }
    30                         closeDoor(floorNow);
    31                         upOrDown = inUpOrDown;
    32                     }
    33                 }
    34                 else {
    35                     tempArrayList = queueOfElevator.pollDown(floorNow);
    36                     if (tempArrayList == null) {
    37                         upOrDown();
    38                         find();
    39                         continue;
    40                     }
    41                     else {
    42                         openDoor(floorNow);
    43                         for (int i = 0;i < tempArrayList.size();i++) {
    44                             peopleInElevator.add(tempArrayList.get(i));
    45                             peopleIn(tempArrayList.get(i).getPersonId(),
    46                                     tempArrayList.get(i).getFromFloor());
    47                         }
    48                         closeDoor(floorNow);
    49                         upOrDown = inUpOrDown;
    50                     }
    51                 }
    52                 upOrDown();
    53                 //find();
    54             }
    55             operate();
    56         }
    57     }
    code run()
     1 private void operate() {
     2         while (peopleInElevator.size() != 0) {
     3             openDoor = false;
     4             someoneDestinates();
     5             if (peopleInElevator.size() == 0)
     6             {
     7                 if (openDoor) {
     8                     closeDoor(floorNow);
     9                 }
    10                 TimableOutput.println("one turn is over");
    11                 break;
    12             }
    13             ArrayList<PersonRequest> tempArrayList;
    14             //TimableOutput.println("here1");
    15             if (upOrDown) {
    16                 tempArrayList = queueOfElevator.pollUp(floorNow);
    17                 if (tempArrayList == null) {
    18                     //TimableOutput.println("no in people");
    19                     if (openDoor) {
    20                         closeDoor(floorNow);
    21                     }
    22                     upOrDown();
    23                     continue;
    24                 }
    25                 else {
    26                     //TimableOutput.println(" in people");
    27                     if (!openDoor) {
    28                         openDoor(floorNow);
    29                     }
    30                     for (int i = 0;i < tempArrayList.size();i++) {
    31                         peopleInElevator.add(tempArrayList.get(i));
    32                         peopleIn(tempArrayList.get(i).getPersonId(),
    33                                 tempArrayList.get(i).getFromFloor());
    34                     }
    35                     closeDoor(floorNow);
    36                 }
    37                 //TimableOutput.println("fuck");
    38             }
    39             else {
    40                 tempArrayList = queueOfElevator.pollDown(floorNow);
    41                 if (tempArrayList == null) {
    42                     //TimableOutput.println("no in people");
    43                     if (openDoor) {
    44                         closeDoor(floorNow);
    45                     }
    46                     upOrDown();
    47                     continue;
    48                 }
    49                 else {
    50                     if (!openDoor) {
    51                         openDoor(floorNow);
    52                     }
    53                     for (int i = 0;i < tempArrayList.size();i++) {
    54                         peopleInElevator.add(tempArrayList.get(i));
    55                         peopleIn(tempArrayList.get(i).getPersonId(),
    56                                 tempArrayList.get(i).getFromFloor());
    57                     }
    58                     closeDoor(floorNow);
    59                 }
    60             }
    61             upOrDown();
    62         }
    63     }
    code operate()
时序图:

 

  时序图看到,除了输出线程在轮询,调度线程在poll时如果队列为空会等待,而电梯线程在准备运行时,如果hashMap为空会等待,而输入线程add之后会唤醒调度线程,调度线程add后会唤醒电梯线程。电梯线程结束前告知调度线程,同时修改信号,调度线程接受信号跳出循环,告知电梯线程,修改信号结束,电梯线程收到信号跳出循环结束。

3.3 同步控制实现

  本次的同步控制完全由synchronizedwait(),notifyAll()实现,即在调度器调用poll时如果队列为空则会wait(),而输入线程add时则会notifyAll,同时为了避免调度器在wait时,输入线程结束而导致其永久的wait,在队列中有一个调度器是否能结束信号,输入线程在结束之前会先将其置true同时notifyAll,而poll则会在被唤醒时判断本信号为true返回null从而让调度器线程结束。此操作在电梯线程上同理。

  本次作业实现了真正的同步控制,而非让线程处于轮询状态。

  本次作业在思考如何让线程最大限度上的wait(),以及在需要被唤醒,和结束的时候正确运行上花了很多功夫。最终有所成果,并且对多线程的控制有所体会。

3.4 度量分析

类表:
Project NamePackage NameType NameNOFNOPFNOMNOPMLOCWMCNCDITLCOMFANINFANOUT
hmwk_6elevatorElevator1001422555200012
hmwk_6elevatorHashMapOfElevator5012111011800030
hmwk_6elevatorInput304337700021
hmwk_6elevatorMainClass001115100-105
hmwk_6elevatorQueueFor3076489100.42857100
hmwk_6elevatorQueueOfInput00105101-130
hmwk_6elevatorScheduler504338800023

  其中NOF (Number Of Fields)代表域个数,NOM (Number Of Methods)代表方法个数,NOPM (Number of Public Methods)代表公共方法个数,LOC (Lines of Code)代表类总代码规模(行数)。

  可以看到Elevator线程的行数极大,其主要原因是没有将电梯的运行逻辑最大的简化与分离,将其分配给调度器。但其实我觉得本次我写的电梯运行逻辑,应该才是一部正常电梯应当具有的基本逻辑。

方法表:
Project NamePackage NameType NameMethodNameLOCCCPC
hmwk_6elevatorElevatorElevator613
hmwk_6elevatorElevatorElevator4990
hmwk_6elevatorElevatorElevator55130
hmwk_6elevatorElevatorElevator820
hmwk_6elevatorElevatorElevator2360
hmwk_6elevatorElevatorElevator2860
hmwk_6elevatorElevatorElevator1620
hmwk_6elevatorElevatorElevator1620
hmwk_6elevatorElevatorElevator1251
hmwk_6elevatorElevatorElevator911
hmwk_6elevatorElevatorElevator911
hmwk_6elevatorElevatorElevator312
hmwk_6elevatorElevatorElevator312
hmwk_6elevatorElevatorElevator620
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator410
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator1721
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator1221
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator1721
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator1221
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator311
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator311
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator1330
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator410
hmwk_6elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_6elevatorInputInput412
hmwk_6elevatorInputInput310
hmwk_6elevatorInputInput1930
hmwk_6elevatorInputInput620
hmwk_6elevatorMainClassMainClass1311
hmwk_6elevatorQueueForQueueFor310
hmwk_6elevatorQueueForQueueFor711
hmwk_6elevatorQueueForQueueFor2030
hmwk_6elevatorQueueForQueueFor310
hmwk_6elevatorQueueForQueueFor311
hmwk_6elevatorQueueForQueueFor311
hmwk_6elevatorQueueForQueueFor410
hmwk_6elevatorQueueOfInputQueueOfInput310
hmwk_6elevatorSchedulerScheduler614
hmwk_6elevatorSchedulerScheduler1640
hmwk_6elevatorSchedulerScheduler310
hmwk_6elevatorSchedulerScheduler620

  分析程序有一些bug无法看到方法名,但是每个类的长度都不长。除了Elevator中的两个有关运行逻辑的方法,run()和operate()。

度量分析表:
Methodev(G)iv(G)v(G)
elevator.Elevator.Elevator(String,Scheduler,HashMapOfElevator)111
elevator.Elevator.closeDoor(int)122
elevator.Elevator.down()133
elevator.Elevator.find()656
elevator.Elevator.getTo(int)155
elevator.Elevator.openDoor(int)122
elevator.Elevator.operate()61313
elevator.Elevator.peopleIn(int,int)111
elevator.Elevator.peopleOut(int,int)111
elevator.Elevator.run()71011
elevator.Elevator.someoneDestinates()356
elevator.Elevator.start()122
elevator.Elevator.up()133
elevator.Elevator.upOrDown()122
elevator.HashMapOfElevator.HashMapOfElevator()111
elevator.HashMapOfElevator.addDown(PersonRequest)122
elevator.HashMapOfElevator.addUp(PersonRequest)122
elevator.HashMapOfElevator.awake()111
elevator.HashMapOfElevator.containDown(int)111
elevator.HashMapOfElevator.containUp(int)111
elevator.HashMapOfElevator.getDieAndAway()111
elevator.HashMapOfElevator.getHashSizeDown()111
elevator.HashMapOfElevator.getHashSizeUp()111
elevator.HashMapOfElevator.pollDown(int)222
elevator.HashMapOfElevator.pollUp(int)222
elevator.HashMapOfElevator.sleepWaiting()335
elevator.Input.Input(String,QueueOfInput)111
elevator.Input.isDown()111
elevator.Input.run()344
elevator.Input.start()122
elevator.MainClass.main(String[])111
elevator.QueueFor.QueueFor()111
elevator.QueueFor.add(PersonRequest)111
elevator.QueueFor.awakeAndGoDie()111
elevator.QueueFor.getQueueSize()111
elevator.QueueFor.poll()334
elevator.QueueFor.printAdd(PersonRequest)111
elevator.QueueFor.printPoll(PersonRequest)111
elevator.QueueOfInput.QueueOfInput()111
elevator.Scheduler.Scheduler(String,QueueOfInput,HashMapOfElevator,Input)111
elevator.Scheduler.isOK()155
elevator.Scheduler.run()345
elevator.Scheduler.start()122
    
ClassOCavgWMC 
elevator.Elevator3.7152 
elevator.HashMapOfElevator1.518 
elevator.Input1.757 
elevator.MainClass11 
elevator.QueueFor1.299 
elevator.QueueOfInput11 
elevator.Scheduler28 
    
Packagev(G)avgv(G)tot 
elevator2.58111 
    
Modulev(G)avgv(G)tot 
hmwk_62.58111 
    
Projectv(G)avgv(G)tot 
project2.58111 

  从表中可以看到本次作业的基本复杂度,模块设计复杂度和求导复杂度都非常正常,除了Elevator.find()与另外两个运行逻辑函数,find()较高是由于使用了对hashMap的便利查找操作,该方法应当写在hashMap类中,而其余两个运行逻辑函数由于调用了多次find(),并且运行逻辑的判断确实过于复杂,从而导致其复杂度较高。但是整体来说还是符合高内聚,低耦合的。

3.5 程序评价

  本次作业基本套用了上次作业的框架,只将轮询改为了waitnotifyAll的操作,同时修改了后一个容器类型从而使电梯操作方便,而且完全重写了电梯的运行逻辑,并且对运行逻辑中可优化的地方进行了优化,比如当电梯运送完最后一个乘客后,不再机械性运行一次,当本楼层同时有上下时,不重复开门,但是仍然漏掉了一点,即在本楼层电梯上人下空后,应判断当前楼层有无人上电梯之后再看是否重新寻找主指令。

  本次作业还是存在一些小bug,在寻找最近主指令时对于其寻找范围少算了1。从而导致了电梯会无法停止的情况。

  同时本次程序的电梯线程的鲁棒性极低,完全由程序结构,以及调度器的正确运行所保证。即没有在电梯线程中写遇到WF的操作。

  对程序进行SOLID 原则分析

  SRP:每个类的职责都很明确,但是有一个方法位置不对,同时有两个方法过于复杂。

  OCP:基本满足。

  LSP:没有子类。

  ISP:未使用接口

  DIP:符合,高层次模块依赖于依赖于低层次的抽象。

四.第三次作业

4.1 设计思路

 

  本次作业仍然采用三线程,即输入线程,调度线程,电梯线程。每两个线程之间使用一个队列容器作为托盘,本次区别是一个调度器将通过三个hashMap类与三个电梯线程沟通,本次作业调度器线程负责将指令依次(指通过if-else强制按顺序)判断,并塞到单一楼层(即该楼层只有该电梯能到)的电梯,不满且直达(指目标和等待楼层该电梯都能到达)的电梯,不满且能捎带的电梯,直达的电梯(不管是否满员,满员则wait()(关于一个调度器如何与三部电梯同步控制,在后面小节中叙述)),能捎带的电梯,B电梯。

  而电梯的运行则是依照第二次作业的运行逻辑,增加了乘客队列,以及对不直达乘客的下电梯判断,指令拆分回扔的内容。

4.2 UML类图与线程运行逻辑与时序图

 

  在本次作业中我的主线程只负责创建线程与实例化对象,只是一个操作线程,而没有实际作用,不予介绍。

  • Input线程

    运行逻辑:不停从系统输入中获取输入,并且addqueueForInput中去。

    死亡条件:当输入为null时结束。

     1 public void run() {
     2         //TimableOutput.println("Running" + threadName);
     3         ElevatorInput elevatorInput = new ElevatorInput(System.in);
     4         while (true) {
     5             PersonRequest request = elevatorInput.nextPersonRequest();
     6             if (request == null) {
     7                 //down = false;
     8                 break;
     9             } else {
    10                 //TimableOutput.println(
    11                 // "add " + request.toString() + "to Input queue");
    12                 queueOfInput.add(request);
    13             }
    14         }
    15         //TimableOutput.println("Fucked " + threadName);
    16         queueOfInput.awakeAndGoDie();
    17         try {
    18             elevatorInput.close();
    19         }
    20         catch (Exception e) {
    21             TimableOutput.println("Fucked " + threadName);
    22         }
    23     }
    code run()
  • Scheduler线程

    运行逻辑:见4.1

    死亡条件:当input线程已结束,并且queueOfInput为空,并且三个电梯线程都没有需要拆分的指令了。

     1  public void run() {
     2         //TimableOutput.println("Running" + threadName);
     3         PersonRequest request;
     4         while (input.isDown()
     5                 || hashMapOfElevatorA.getNumOfBadRequest() != 0
     6                 || hashMapOfElevatorB.getNumOfBadRequest() != 0
     7                 || hashMapOfElevatorC.getNumOfBadRequest() != 0
     8                 || queueOfInput.getQueueSize() != 0) {
     9             //TimableOutput.println("sc not die");
    10             request = queueOfInput.poll();
    11             if (request == null) {
    12                 break;
    13             }
    14             distribute(request);
    15         }
    16         //TimableOutput.println("scheduler died");
    17         hashMapOfElevatorA.awake();
    18         hashMapOfElevatorB.awake();
    19         hashMapOfElevatorC.awake();
    20     }
    code run()
  • Elevator线程

    运行逻辑:在其为空时寻找一个最近楼层的指令作为主指令,接到主指令之后以主指令运行方向,过程中取出同层的同向等待指令,直到电梯为空,寻找下一个主指令。

    死亡条件:当input线程与scheduler线程已结束,并且queueOfInputhashMapOfElevator为空。

     1  public synchronized void run() {
     2         //TimableOutput.println("Running " + threadName);
     3         PersonRequest request;
     4         while (scheduler.isOK()) {
     5             //TimableOutput.println("elevator waiting");
     6             queueOfElevator.sleepWaiting();
     7             //TimableOutput.println("elevator awake");
     8             if (queueOfElevator.getDieAndAway()
     9                     && queueOfElevator.getHashSizeDown() == 0
    10                     && queueOfElevator.getHashSizeUp() == 0) {
    11                 break;
    12             }
    13             find();
    14             while (peopleInElevator.size() == 0) {
    15                 ArrayList<PersonRequest> tempArrayList;
    16                 if (inUpOrDown) {
    17                     tempArrayList = queueOfElevator.pollUp(floorNow);
    18                     if (tempArrayList == null) {
    19                         upOrDown();
    20                         find();
    21                         continue;
    22                     } else {
    23                         openDoor(floorNow);
    24                         for (int i = 0; i < tempArrayList.size(); i++) {
    25                             peopleInElevator.add(tempArrayList.get(i));
    26                             peopleIn(tempArrayList.get(i));
    27                         }
    28                         closeDoor(floorNow);
    29                         upOrDown = inUpOrDown;
    30                     }
    31                 } else {
    32                     tempArrayList = queueOfElevator.pollDown(floorNow);
    33                     if (tempArrayList == null) {
    34                         upOrDown();
    35                         find();
    36                         continue;
    37                     } else {
    38                         openDoor(floorNow);
    39                         for (int i = 0; i < tempArrayList.size(); i++) {
    40                             peopleInElevator.add(tempArrayList.get(i));
    41                             peopleIn(tempArrayList.get(i));
    42                         }
    43                         closeDoor(floorNow);
    44                         upOrDown = inUpOrDown;
    45                     }
    46                 }
    47                 upOrDown();
    48             }
    49             operate();
    50         }
    51     }
    code run()

     

     1 private void operate() {
     2         while (peopleInElevator.size() != 0) {
     3             openDoor = false;
     4             someoneDestinates();
     5             if (peopleInElevator.size() == 0) {
     6                 if (openDoor) {
     7                     closeDoor(floorNow);
     8                 }
     9                 //TimableOutput.println("one turn is over");
    10                 break;
    11             }
    12             ArrayList<PersonRequest> tempArrayList;
    13             //TimableOutput.println("here1");
    14             if (upOrDown) {
    15                 tempArrayList = queueOfElevator.pollUp(floorNow);
    16                 if (tempArrayList == null) {
    17                     //TimableOutput.println("no in people");
    18                     if (openDoor) {
    19                         closeDoor(floorNow);
    20                     }
    21                     upOrDown();
    22                     continue;
    23                 } else {
    24                     //TimableOutput.println(" in people");
    25                     if (!openDoor) {
    26                         openDoor(floorNow);
    27                     }
    28                     for (int i = 0; i < tempArrayList.size(); i++) {
    29                         peopleInElevator.add(tempArrayList.get(i));
    30                         peopleIn(tempArrayList.get(i));
    31                     }
    32                     closeDoor(floorNow);
    33                 }
    34                 //TimableOutput.println("fuck");
    35             } else {
    36                 tempArrayList = queueOfElevator.pollDown(floorNow);
    37                 if (tempArrayList == null) {
    38                     //TimableOutput.println("no in people");
    39                     if (openDoor) {
    40                         closeDoor(floorNow);
    41                     }
    42                     upOrDown();
    43                     continue;
    44                 } else {
    45                     if (!openDoor) {
    46                         openDoor(floorNow);
    47                     }
    48                     for (int i = 0; i < tempArrayList.size(); i++) {
    49                         peopleInElevator.add(tempArrayList.get(i));
    50                         peopleIn(tempArrayList.get(i));
    51                     }
    52                     closeDoor(floorNow);
    53                 }
    54             }
    55             upOrDown();
    56         }
    57     }
    code operate()
时序图:

 

  时序图画的比较乱,大体与第二次作业类似,几个线程之间的唤醒与结束,但是多了一个调度线程调度时阻塞,被电梯线程唤醒继续工作这一流程。

4.3 同步控制实现

  第二次的作业控制如下:

  本次的同步控制完全由synchronized与wait(),notifyAll()实现,即在调度器调用poll时如果队列为空则会wait(),而输入线程add时则会notifyAll,同时为了避免调度器在wait时,输入线程结束而导致其永久的wait,在队列中有一个调度器是否能结束信号,输入线程在结束之前会先将其置true同时notifyAll,而poll则会在被唤醒时判断本信号为true返回null从而让调度器线程结束。此操作在电梯线程上同理。

  而本次作业在这基础上考虑到现在不止是输入线程能写入队列,电梯也可以,故而调度器结束信号还包括了电梯是否都不含有需要拆分回扔的指令。

  而在保证了调度器线程的存活之后,电梯线程的结束判断则可以和上次相同了。

  再来说说如何让一个调度器面对三个电梯都有可能因为向电梯hashMap插入指令,而因满员wait的情况下如何调度,我的处理是让电梯线程遇到满员并且wait()时,不适用while{wait()},而是if{wait()},这样任何一个电梯减员后将调度线程唤醒之后,电梯再判断一次当前塞请求的电梯是否满员,如果满员则将该指令不做任何处理原样返回,重新插入到Input队列中去。这样保证了电梯能分别被三个电梯阻塞,而且能够被正常唤醒运行,并且避免出现轮询的情形。

4.4 度量分析

类表:
Project NamePackage NameType NameNOFNOPFNOMNOPMLOCWMCNCDITLCOMFANINFANOUT
hmwk_7elevatorElevator1501623307000013
hmwk_7elevatorHashMapOfElevator80201916232000.130
hmwk_7elevatorInput304337700021
hmwk_7elevatorMainClass001129100-105
hmwk_7elevatorQueueFor60987517100.33333300
hmwk_7elevatorQueueOfInput00105101-140
hmwk_7elevatorScheduler1301432466200023

  其中NOF (Number Of Fields)代表域个数,NOM (Number Of Methods)代表方法个数,NOPM (Number of Public Methods)代表公共方法个数,LOC (Lines of Code)代表类总代码规模(行数)。

  本次作业的Elevator类内容并没有增加太多,增加了一些对于满员和拆分回扔的内容,而调度器内容则激增,也说明了本次作业主要的问题在调度器上,同时也说明了架构的成功,让电梯与调度器功能分离的比较合理。

方法表:
Project NamePackage NameType NameMethodNameLOCCCPC
hmwk_7elevatorElevatorElevator1739
hmwk_7elevatorElevatorElevator4990
hmwk_7elevatorElevatorElevator54130
hmwk_7elevatorElevatorElevator820
hmwk_7elevatorElevatorElevator2360
hmwk_7elevatorElevatorElevator2860
hmwk_7elevatorElevatorElevator1620
hmwk_7elevatorElevatorElevator1620
hmwk_7elevatorElevatorElevator1251
hmwk_7elevatorElevatorElevator911
hmwk_7elevatorElevatorElevator911
hmwk_7elevatorElevatorElevator511
hmwk_7elevatorElevatorElevator1431
hmwk_7elevatorElevatorElevator831
hmwk_7elevatorElevatorElevator39111
hmwk_7elevatorElevatorElevator620
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator511
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator2941
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator1221
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator2941
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator1221
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator311
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator311
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator1640
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator410
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator410
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator311
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator820
hmwk_7elevatorHashMapOfElevatorHashMapOfElevator310
hmwk_7elevatorInputInput412
hmwk_7elevatorInputInput310
hmwk_7elevatorInputInput1930
hmwk_7elevatorInputInput620
hmwk_7elevatorMainClassMainClass2711
hmwk_7elevatorQueueForQueueFor310
hmwk_7elevatorQueueForQueueFor711
hmwk_7elevatorQueueForQueueFor2030
hmwk_7elevatorQueueForQueueFor310
hmwk_7elevatorQueueForQueueFor311
hmwk_7elevatorQueueForQueueFor311
hmwk_7elevatorQueueForQueueFor410
hmwk_7elevatorQueueForQueueFor1241
hmwk_7elevatorQueueForQueueFor1241
hmwk_7elevatorQueueOfInputQueueOfInput310
hmwk_7elevatorSchedulerScheduler816
hmwk_7elevatorSchedulerScheduler1330
hmwk_7elevatorSchedulerScheduler821
hmwk_7elevatorSchedulerScheduler621
hmwk_7elevatorSchedulerScheduler1741
hmwk_7elevatorSchedulerScheduler1741
hmwk_7elevatorSchedulerScheduler1741
hmwk_7elevatorSchedulerScheduler1541
hmwk_7elevatorSchedulerScheduler45141
hmwk_7elevatorSchedulerScheduler2571
hmwk_7elevatorSchedulerScheduler2571
hmwk_7elevatorSchedulerScheduler2571
hmwk_7elevatorSchedulerScheduler410
hmwk_7elevatorSchedulerScheduler620

  分析程序有一些bug无法看到方法名,但是每个类的长度都不长。除了Elevator中的两个有关运行逻辑的方法,run()和operate(),以及Scheduler中的任务分配函数。

度量分析表:
Methodev(G)iv(G)v(G)
elevator.Elevator.Elevator(String,Scheduler,QueueOfInput,HashMapOfElevator,ArrayList<Integer>,ArrayList<Integer>,int,int,String)133
elevator.Elevator.badRequestOut(PersonRequest)9611
elevator.Elevator.closeDoor(int)122
elevator.Elevator.down()133
elevator.Elevator.find()656
elevator.Elevator.getTo(int)155
elevator.Elevator.isBadRequest(PersonRequest)323
elevator.Elevator.openDoor(int)122
elevator.Elevator.operate()61313
elevator.Elevator.peopleIn(PersonRequest)111
elevator.Elevator.peopleOut(PersonRequest)133
elevator.Elevator.run()71011
elevator.Elevator.someoneDestinates()3910
elevator.Elevator.start()122
elevator.Elevator.up()133
elevator.Elevator.upOrDown()122
elevator.HashMapOfElevator.HashMapOfElevator(int)111
elevator.HashMapOfElevator.addDown(PersonRequest)245
elevator.HashMapOfElevator.addNumOfBadRequest()111
elevator.HashMapOfElevator.addNumOfPeople()111
elevator.HashMapOfElevator.addUp(PersonRequest)245
elevator.HashMapOfElevator.awake()111
elevator.HashMapOfElevator.containDown(int)111
elevator.HashMapOfElevator.containUp(int)111
elevator.HashMapOfElevator.getDieAndAway()111
elevator.HashMapOfElevator.getHashSizeDown()111
elevator.HashMapOfElevator.getHashSizeUp()111
elevator.HashMapOfElevator.getNumOfBadRequest()111
elevator.HashMapOfElevator.getNumOfPeople()111
elevator.HashMapOfElevator.isFull()212
elevator.HashMapOfElevator.pollDown(int)222
elevator.HashMapOfElevator.pollUp(int)222
elevator.HashMapOfElevator.setMaxOfNum(int)111
elevator.HashMapOfElevator.sleepWaiting()347
elevator.HashMapOfElevator.subNumOfBadRequest()111
elevator.HashMapOfElevator.subNumOfPeople()111
elevator.Input.Input(String,QueueOfInput)111
elevator.Input.isDown()111
elevator.Input.run()344
elevator.Input.start()122
elevator.MainClass.main(String[])111
elevator.QueueFor.QueueFor()111
elevator.QueueFor.add(PersonRequest)111
elevator.QueueFor.awakeAndGoDie()111
elevator.QueueFor.elevatorWantAlive(String)134
elevator.QueueFor.elevatorWantToDie(String)134
elevator.QueueFor.getQueueSize()111
elevator.QueueFor.poll()337
elevator.QueueFor.printAdd(PersonRequest)111
elevator.QueueFor.printPoll(PersonRequest)111
elevator.QueueOfInput.QueueOfInput()111
elevator.Scheduler.Scheduler(String,QueueOfInput,HashMapOfElevator,HashMapOfElevator,HashMapOfElevator,Input)111
elevator.Scheduler.canTakeA(PersonRequest)747
elevator.Scheduler.canTakeB(PersonRequest)747
elevator.Scheduler.canTakeC(PersonRequest)747
elevator.Scheduler.distribute(PersonRequest)122
elevator.Scheduler.distributeOther(PersonRequest)13232
elevator.Scheduler.distributeSingle(PersonRequest)144
elevator.Scheduler.inSingle(PersonRequest)234
elevator.Scheduler.isOK()199
elevator.Scheduler.operateA(PersonRequest)144
elevator.Scheduler.operateB(PersonRequest)144
elevator.Scheduler.operateC(PersonRequest)144
elevator.Scheduler.run()367
elevator.Scheduler.start()122
    
ClassOCavgWMC 
elevator.Elevator4.3870 
elevator.HashMapOfElevator1.632 
elevator.Input1.757 
elevator.MainClass11 
elevator.QueueFor1.8917 
elevator.QueueOfInput11 
elevator.Scheduler4.4362 
    
Packagev(G)avgv(G)tot 
elevator3.72242 
    
Modulev(G)avgv(G)tot 
hmwk_73.72242 
    
Projectv(G)avgv(G)tot 
project3.72242 

  本次基本与上次类似,但是有一个及其巨大的函数,是调度器的分配函数,由于该函数内含16个if-else连续判断,并且每个条件至少2个因子的连接,从而导致其复杂度及其巨大,而这可以通过判断中间过程是否执行,来拆分降低其复杂度。或者通过改变架构,让三个电梯争抢同一容器内的指令来取消巨大的if-else链。

4.5 程序评价

  本次作业基本套用了上次作业的框架,只是在判断是否满员,在调度器的决策上改动较多,本次最大的问题体现在架构的不够优秀上,该架构确实在电梯与调度器的功能划分上表现得比较优秀,但是在细节决策上思考不够仔细,而导致了后期工作经常卡壳。

  比如对可拆分指令的拆分回写,对于其拆分点的决策思考不够仔细,从而导致了需要打很多补丁来保证其正确性。

  同时本次的鲁棒性还是很差,没有在电梯接受指令的WF上进行考虑,选择了完全相信调度器的正确性。

  本次作业的性能不是非常好,思考了一下可能是由于调度器的硬性if-else链的僵硬性导致的,可以选择形成一个电梯共同指令池,电梯去争抢指令可能会提高效率,但是由于这样会违背我的初衷”让电梯和调度器做自己的事“,或者让调度器能够知道电梯的当前状态从而精准分配。

  同时本次程序的电梯线程的鲁棒性极低,完全由程序结构,以及调度器的正确运行所保证。即没有在电梯线程中写遇到WF的操作。

  对程序进行SOLID 原则分析

  SRP:每个类的职责都很明确,但是有一个方法位置不对,同时有三个方法过于复杂。

  OCP:基本满足。

  LSP:没有子类。

  ISP:未使用接口

  DIP:符合,高层次模块依赖于依赖于低层次的抽象。

五. 分析BUG

  由于基本通过了强测,除了第6次作业有一个点由于硬编码出现bug。

  而谈谈在编程时遇到的bug,绝大多数甚至90%出现在线程在不该结束的时候结束上,而对共享变量的维护由于使用了sychronized所以没有出现过bug。

  而出现线程结束的原因主要有两个:

    1.没弄清楚其结束的条件到底是什么,或者说是该线程有必要活下去的条件。

    2.以为弄清楚了结束条件,但是没有考虑到线程调度过程中会出现的状况,而没有将条件补充到最完备的情况。

  同时还出现了初始化线程时随意初始化参数,而在之后的线程实例化时再去set,而太低估线程运行的速度导致bug。

  此附上一个判断程序正确的脚本:

import re
import time
import sys
if __name__ == '__main__':
  with open('*.txt', 'r') as f:
      total_time = 0
      for line in f.readlines():
          matchObj = re.match(r'\[(.*)\](.*)', line, re.M|re.I)
          cur_time = float(matchObj.group(1))
          time.sleep(cur_time-total_time)
          print(matchObj.group(2))
          sys.stdout.flush()
          total_time = cur_time

  此脚本按照时间戳输出指令,并且通过管道传输给程序,来模拟评测机上的输出。

六.心得体会

  本次作业的每次变化第一次在于让电梯本身的运行变得真实,第二次在于让调度器变得真实,而本次作业也确实让我感受到了一个优秀的架构,能够让你扩展起来省下很多功夫,就像本次作业我的架构能让我第二次作业90%的精力在写电梯的运行,第三次作业80%的精力在写调度器的运行。但是也让我体会到了一个优秀的架构是同时能兼并正确与性能的,也由看到我的架构不够好而产生的限制,同时也体会到多线程编程的困难程度,从而要对多线程中需要注意的地方多加上心。

转载于:https://www.cnblogs.com/Sraey7-blog/p/10764804.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值