BUAA-OO第二单元-双轿厢多线程电梯

本文详细探讨了在电梯调度系统中如何使用同步块和锁来管理共享资源,包括对方法、实例化共享对象和类的锁定策略。文章还介绍了分级调度算法,如多电梯协作和单电梯LOOK策略,以及如何通过生产者-消费者模型进行线程协作。最后,作者分享了在实践中遇到的bug修复和调试技巧,强调了线程安全和层次化设计的重要性。
摘要由CSDN通过智能技术生成

Synchronized-同步块与锁

1. 对所有涉及共享对象的方法都加锁! 2. 遍历访问共享对象时,还应该对遍历语句块涉及到的实例化共享对象加锁(即设置为同步块)! 3. 如果共享对象被封装成了一个类,可以对该类中的方法加锁 (类比OS中的管程) ,使方法内部的语句运行不会受到其他线程语句的干扰; 如果共享对象不是单独的一个类,就需要对涉及共享对象的操作的语句块加锁 (类比OS中的信号量实现互斥),使不同地方对其的访问是互斥的。
  • 同步块加锁实例:

InputThread类

synchronized (totalList) {
    RequestList requestList = totalList.
            get(((NormalResetRequest) request).getElevatorId());
    requestList.addRequest((NormalResetRequest) request);
}

Allocate类

synchronized (totalList.get(i)) {
    totalList.get(i).setEnd(true);
}

synchronized (totalList) {
    totalList.get(id).addRequest(request);
}

调度策略:分级调度算法

1. 多电梯协作:使用自己构造的策略分配请求

在选择最合适的电梯时:若普通电梯正在重置,跳过;若为双轿厢电梯,则选择A或B中的一部进行判断。
设置index为:该部电梯到乘客起始位置的有向长度 / 该电梯的运行速度。
若该电梯接收到该乘客后达到人数上限,则令index为正无穷。

若该电梯的请求列表为空,将系数index减少,增加其优先级。

选择index最小的电梯接受请求。

若当前没有可用的电梯, sleep(50); 后将其重新放入总请求池 WaitListOfAll

若某电梯接受到的乘客 >= 其最大容量,则让其休息2秒后再接受请求。该策略可以有效防止一部电梯接收到过多请求。

对于双轿厢电梯,考虑到其省电的特性与可能增加的多次换乘相互抵消,故在我的架构中没有改变双轿厢电梯的优先级。

  • 该自定义策略可以较好地平衡耗电量,乘客等待时间等指标,得到良好的性能分数。

2. 单电梯运行策略:使用LOOK策略处理请求

    motion = Drive.askForNextMotion(curFloor, capacity,
     passengerNum, directionOfNext, requestList);
    switch (motion) {
        case MOVE:
            move(moveTime);
            break;
        //other case: ……
    }

在 Motion 类中实现单部电梯的 LOOK 运行策略。

3. 调度线程与其他线程的交互

Allocate 调度器线程传入了总请求池(waitListOfAll),分电梯请求表(totalList),总电梯(elevators)三个共享对象。其功能是把 waitListOfAll 中的请求按照分配策略,有序地分配到 totalList 中。同时,需要通过 elevators 得到电梯的一些属性(比如是否在重置,最大容量等),来进行合适的分配。

在电梯线程中,调用 Motion 类的 askForNextMotion 函数,传入电梯当前的参数(属性)以得到下一步的行动。

注意对共享对象的访问是互斥的(借用OS中的概念),需要加锁。

最终主要结构 在这里插入图片描述

Main类

构造并启动线程:输入线程,分配器线程,六个电梯线程。

InputThread类

若输入结束,退出线程并将输出信号传给 WaitListOfAll
若获得普通请求,将其加入调度器。
若获得重置请求,立即 将其加入对应电梯的 requestList

Allocate类-分配器

若可以结束( WaitListOfAll 为空且已结束,重置请求全部处理完使用 ResetRequestCount 共享类进行统计,普通电梯不在重置,双轿厢电梯请求表均为空),向电梯发送结束信号并退出。
若暂时从 WaitListOfAll 中得到的请求为空: sleep(50 后重新判断;
采用分配策略将请求加入电梯的 requestList

Elevator类-电梯

若可以结束(该电梯的请求表为空且已结束且该电梯不处于重置状态,或者该电梯已被重置为双轿厢电梯),直接退出。
优先处理重置请求。
之后,像Drive类(单电梯运行策略)**“询问”**下一步的行动,并执行。
优化:

  1. 电梯在顶楼或底层开门,自动转变运行方向,避免二次开关门
  2. 若接收到RESET指令时电梯处于开关门状态,则直接启动重置,避免二次开关门

DcElevator类 extends Elevator-双轿厢电梯

继承电梯类,实现双轿厢独立运行。
在换乘楼层时特判,实现两轿厢互不相撞。

架构与迭代

迭代与扩展

在这里插入图片描述

第一次作业实现了单电梯的基本运行,分配策略由输入提供。
第二次作业实现了多电梯分配策略和普通RESET请求
第三次作业实现了双轿厢电梯重置请求
之后,还可以实现开关门防止夹人,可以调整运行模式(高峰模式,深夜模式等)。因为本架构具有优秀的扩展性,实现了层次化设计。
在这里插入图片描述

在这里插入图片描述

线程协作

通过生产者-消费者模式,实现请求的分配:生产者是输入线程,消费者是电梯线程,中间的托盘是请求表。

稳定与易变内容

调度策略、电梯种类、请求种类等,属于易变的内容,需要根据需求的调整而改变
请求表类(RequestList),电梯开关门、上下乘客的方法等,属于稳定的内容

双轿厢的配合

通过 TransferController 类,使换乘楼层处仅允许一部电梯到达。
到达换乘楼层时,将 共享变量“状态” 设为 OCCUPIED ;离开换乘楼层,将共享变量“状态”设为 FREE

// in Class DcElevator
if (super.getCurFloor() == transferFloor) {
    //实现清空轿厢内所有乘客,反转运行方向,接受该层乘客,然后立即离开换乘楼层
    clearReverseGetInAndLeave();
}

// in Class TransferController
    public synchronized void setOccupied() {
        waitRelease();
        state = State.OCCUPIED;
        notifyAll();
    }

    public synchronized void setFree() {
        state = State.FREE;
        notifyAll();
    }

    private synchronized void waitRelease() {
        notifyAll();
        while (state == State.OCCUPIED) {
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
TransferController 类,使用类似**OS中管程**的思想,实现对共享变量的修改,保证两轿厢**互斥地到达**换乘楼层。

bug与debug

bug分析

第一次作业中:没有在遍历访问共享对象时加锁。
第二次作业中:

  1. 没有立即处理RESET请求,导致收到重置信号后可能不会立马处理,电梯继续运行
  2. 与重置有关的共享变量 isReset 没有加锁,导致多线程冲突
  3. 修复了乘客进入条件,防止一个乘客多次进入电梯
  4. 删除不必要的锁,防止误锁导致的某段时间内共享对象无法被访问,因而出现逻辑错误的问题和死锁问题。

第三次作业中:

  1. 睡0.01秒防止逻辑问题
  2. 共享对象没有加锁,导致多线程冲突

debug方法

  1. 通过print打印法,观察线程能否正常结束
  2. 通过构造高强度或极端数据,检测边界情况
  3. 通过观察大法,分析加锁的逻辑

心得体会

  1. 线程安全方面,我基本掌握了多线程编程,线程协作的方法。了解了在合适的地方加锁,既使程序高速运行,又保证多线程不会互相干扰影响。此外,我还知道了读写锁和普通锁的基本使用方法。
  2. 层次化设计方面,本次电梯作业进一步加深了我对面向对象编程的理解,把“高内聚,低耦合”的原则落实到写代码中。
  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值