BUAA OO UNIT2单元总结

黄弘烨-22371430 学生 2024-06-19 19:32:39

同步块的选择

由于我架构的设计,整个代码中存在三种队列:waitingQueueRequestQueueLeaveQueue,这三个队列就是整个架构中最核心的“临界资源”。

具体体现为,waitingQueue链接了InputScheduler,而RequestQueue链接了SchedulerElevator,每个队列都处在两层结构之间的“临界区域”。

这种架构最优秀的地方,在于同步块会很集中,也就不容易遗漏。

每次访问各种Queue时,仅需要对Queue上锁,并且为队列中的每个方法上锁,即可确保各种类之间不会出现数据竞争。

也正是这种清晰的代码结构,给我的代码带来了很强的生命力,极其容易阅读、理解和维护。

调度器的设计

调度器通过waitingQueue链接着Input,通过RequestQueue链接着Elevator,是整个代码的枢纽所在。正如前文所述,他与其他类的交互,正是通过waitingQueueRequestQueue这两个线程安全的类实现的,也就不存在任何的线程安全问题。

public class Schedule extends Thread {
    private WaitingQueue waitingQueue;
    private ArrayList<RequestQueue> processingQueues;
    private ArrayList<Elevator> elevators;

Input中的数据会通过waitingQueueaddRequest方法移到调度器中,再通过各种线程安全方法通过队列转移到Elevator,实现了工程流水线的封装。

调度策略:整体的调度策略是以时间为主要核心的。通过影子电梯的模拟方式,提前计算出每个电梯需要花费的时间,再对每个请求进行分配。

纵观整个单元,调度器是最稳定的一个类之一,仅次于输入流。在第一次迭代时,仅仅只是更改了调度策略,从固定调度改为计算时间后分配。修改的代码量仅有数行(计算过程被封装在电梯的函数中);而第二次迭代则完全没有任何变化。

电梯的不相碰

  1. 最主要的一点在于对换乘楼层的互斥访问,通过设立一个mutex信号量,保证每次仅有一个电梯访问换乘区,另一个电梯必须等待信号量释放。

    public synchronized void moveToNextFloor() {
         int nextFloor = curFloor + direction;
         int oldFloor = curFloor;
         if (type != 0 && nextFloor == trans) {
             if (isTrans[id] == 0) {
                 isTrans[id] = 1;
             } else {
                 while (isTrans[id] == 1) {
                     try {
                         this.wait();
                     } catch (Exception e) {
                         e.printStackTrace();
                     }
                 }
             }
         }
    
         this.curFloor = nextFloor;
         sleepForATime((long)(moveTime * 1000));
         output("ARRIVE", 0);
    
         if (type != 0 && oldFloor == trans) {
             if (isTrans[id] == 1) {
                 isTrans[id] = 0;
                 synchronized (otherElevator) {
                     otherElevator.notify();
                 }
             }
         }
     }
    
  2. 其次保证不相碰的一点就是让电梯及时让位。通过让电梯在空闲时移出临界区,来为移动的电梯腾开换乘区的位置

    if (inQueue.isEmpty()) {
                 synchronized (processingQueue) {
                     if (processingQueue.isEmpty() && type != 0 && curFloor == trans) {
                         setDirection();
                         moveToNextFloor();
                     } 
    

bug相关

最明显的bug莫过于与程序结束相关的bug。在第一次作业中,我们注意到waitingQueue仅仅来源于输入流,当输入结束时,会引起等待队列--处理中队列一系列的结束,最终让程序结束。

但是这跟第二次作业时不同的。在第二次作业中,waitingQueue还有一种来源——reset释放后的乘客。如果不加以处理,就会导致程序结束时还有人没有送完的情况。

而第三次作业又多了一条路径,也就是换乘的乘客。

这三条路径需要对应三种不同的end信号。实现这个end信号的过程需要一点技巧,也算是这个单元很有趣的bug修复过程。

debug相关

这个单元的debug极其折磨。由于无法使用断点等办法,只能使用手动输出的方式获取程序运行的路径;但是程序的运行又极其的慢,修改一次输出信息,就要重新跑数十秒。目前只有通过一些黑科技来加快“时间流速”,其他的好办法一概不知,希望uu来点建议。

线程安全相关

线程一旦出现问题,不管是debug的成本,还是发现问题后修复的成本都极其高(而且对心态打击极大),所以每次写代码时必须谨慎(不然一旦出现线程安全问题就容易跟我一样开摆)

而这次我的作业极其符合层次化设计,四层大类+三条线程安全连接的数据结构,让我的代码极其清晰而易于维护,我将学习这次的经验,并且将这次做的好的地方继续发扬下去。

...全文
107 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

302

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧