BUAA_OO 第二单元作业-电梯调度

一、同步块的设置和锁

我的设计并没有完全实现类的封装,使用的类并非完全线程安全。在WaitingQueue中,我尝试对WaitingQueue的take和put操作进行上锁。然而实际上由于我采用了CopyOnWriteArrayList,这是完全不必要的。这也是在逐渐摸索中发现的。

    private final CopyOnWriteArrayList<PersonRequest> personRequests;
    public void addPersonRequest(PersonRequest p) {
        try {
            personRequests.add(p);
            this.notifyAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public PersonRequest take() {
        while (personRequests.isEmpty()) {
            try {
                personRequests.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return personRequests.remove(0);
    }

例如以上代码,其中同步块关键词synchronized是没有必要的,但是我在初次设计的时候由于对线程安全类不熟悉,所以并没有写出很漂亮的代码。

那么实际上,要向personRequests添加或者删除请求,都是可以直接采用CopyOnWriteArrayList提供的接口的。这也提示我们,如果自己要设计一个线程安全类,一定要在内部实现同步,而不是给代码编写者。这样符合面向对象的思维。

同步块的实现是为了防止在两个线程读取同一块内存时发生冲突。例如: Thread A 试图读取 class MyClass 内容,而同时 Thread B 试图修改 Class MyClass 内容,那么 Thread A 既可能读取B修改前的内容,也可能读取 Thread B 修改后的内容,导致的访问的不稳定。我们采用 synchronized 关键字,可以在某一个线程访问时拒绝其他线程访问对应对象,有效避免混乱的发生。

需要注意的是,如果某一线程同步访问某一地址,然而由于种种原因无法获取对象的锁,会陷入“占着茅坑不拉屎”的尴尬局面,持续占用处理器资源而不做事。因此我们采用 waitnotify 的方式在恰当的时机放弃处理器资源的占用。

二、调度器设计

考虑到三种电梯的运行效率问题,我们将依次分配C、B、A类型电梯,如果分配成功则返回true,若失败则返回false。

判断是否分配一辆电梯的原则:

  • 若方向相同且乘客出发楼层在电梯运行途径中,则捎带乘客。
  • 若电梯停止且有乘客发出请求,则无条件接待乘客。
    private boolean allocateElevator() {
        PersonRequest personRequest = waitingQueue.take();
        ProcessingInteraction best = null;

        int from = personRequest.getFromFloor();
        int to = personRequest.getToFloor();

        boolean flag = allocateTypeC(personRequest);
        if (flag) {
            return true;
        }
        flag = allocateTypeB(personRequest);
        if (flag) {
            return true;
        }

        for (ProcessingInteraction item : processingInteractions) {
            if (item.getElevatorType().equals("A") && !item.isFull()) {
                if (item.getStateOfElevator() == StateOfElevator.DOWN && to < from
                        || item.getStateOfElevator() == StateOfElevator.UP && to > from
                        || item.getStateOfElevator() == StateOfElevator.WAIT) {
                    best = item;
                }
            }
        }
        if (best == null) {
            waitingQueue.addPersonRequest(personRequest);
            return false;
        }
        best.addNewRequest(personRequest);
        return true;
    }

三、架构设计

设计思路

MainClass:在主类中初始化时间戳,并且调用控制台输入类。
Input:控制输入,初始化3个电梯并运行;初始化调度器并运行。
Scheduler:调度器类。根据输入指令将乘客请求指派电梯或者电梯请求增加电梯。
Elevator:电梯类。使用look算法,根据乘客请求运送。
WaitingQueue:等待分配队列。输入的乘客请求加入到其中,等待scheduler分配电梯。
ProcessingInteraction:处理队列。调度器将乘客分配给某个电梯,等待电梯处理。





四、BUG与DEBUG

本次作业由于对于多线程的运行机制不清晰,前期主要bug来自于同步块的设置问题,不敢说时bug,只能说是一窍不通。我的第一次和第二次作业都没有完成。在第三次作业前,我终于理解了同步和锁机制,那么剩下的bug其实很简单。我对提供的数据点进行逐个调试(print方式),查询是否有死循环和调度错误,逐个修复。由于不会按时间戳输入代码,我采用了评论区的python代码,由于答题设计思路正确,只需逐步调试即可。

五、体会

第二单元作业是对于Java多线程的应用,模拟一个生活实际问题。本单元作业的难点(对我而言)在于理解并发机制。但是由于时间受限、基础知识不理解等原因导致我前两次作业均为能完成。实际上我仍然每周花至少20小时来学习这门课程,但是无奈没有好的教程和合适的辅导者,以至于我遇到问题甚至不知道如何解决。后来我经历了十分艰难的请教和学习,问过很多朋友才最终解决了锁机制的理解问题,继而做出了作业。

由于没有前两次作业的结果,我没有任何重复设计,也没有改进,谈不上优化。

总而言之,希望接下来努力学习,按时完成作业。

posted on 2021-04-23 12:27  Teacc  阅读(85)  评论(1)    收藏  举报