2024-BUAA-OO-U2多线程总结

一、三次作业

1.1 hw5

1.1.1 UML类图

在这里插入图片描述

  • 本次作业中,我借鉴了OO上机时的代码架构,设计了3个线程类和1个共享类。

  • 三个线程类分别为输入线程 InputThread 、调度线程 Schdeuler 、电梯线程 Elevator ,一个共享类为 RequesQueue 运行过程中, Main 开启全部的线程(包括 1 个InputThread、1 个Schdeuler 、6 个 Elevator),并定义了 6 个共享对象RequesQueue。

1.1.1 同步块设置与锁的选择

  • 完成这次作业的时候其实我对同步块、锁这些概念理解的并不是特别透彻,凡是调度器中或者电梯中用到共享对象(即侯乘表)时,我都设置了很大的同步块;同时,侯乘表类中的所有方法也都加上了 synchronized 修饰词。

1.1.3 调度器设计

  • 本次作业指定电梯,不需要考虑人员的分配问题。而对于电梯的运行策略,我采用的是LOOK策略。这好像没什么好多说的。

  • 线程间的通信都是通过共享对象来进行的,即InputThread与Scheduler共享一个侯乘表,请求到时就由InputThread放入侯乘表,同时notifyAll()唤醒等待着的Scheduler,Scheduler将请求从侯乘表中取走。Scheduler与Elevator间的通信也是如此。线程图如下

在这里插入图片描述

1.2 hw6

1.2.1 UML类图

在这里插入图片描述

  • 本次作业整体架构与第一作业相比没有比较大的变化,只不过新增了Person类以便更好处理官方标准输入包中的PersonRequest

1.2.1 同步块设置与锁的选择

  • 同步块基本上没多大变化,只不过考虑到ResetRequest,电梯里的数据结构从原先的一个侯乘表对象与乘客的ArrayList,变为了一个侯乘表对象、一个Receive后的还未上电梯的乘客ArrayList,和在电梯上的乘客ArrayList。这样反而导致电梯中的同步块减少,没有太多的对共享对象的读写操作。

1.2.3 调度器设计

  • 我加入了随机神教。嘿嘿。

  • 又由于我让吐出去的乘客由原先的电梯接受,所以线程图没有基本没变。

在这里插入图片描述

1.3 hw7

1.3.1 UML类图

在这里插入图片描述

  • 本次新增了三个类,分别是:

    1. 单例模式的Counter类,用来判断进程结束条件

    2. 共享对象的Flag类,用来确保双轿厢电梯不同时进入换乘层

    3. 线程DoubleElevator类,用来满足双轿厢电梯需求

1.3.2 同步块设置与锁的选择

  • 由于电梯间的换乘会导致无法简单地通过InputThread的结束来判断是否该setEnd(),我运用了单例模式的计数器(具体实现参考了OS课程中的信号量的思路)来判断是否需要结束。不得不说,单例模式实在是非常适合多线程的应用场景,使用时不需要考虑线程安全的问题。
public class Counter {
    private static Counter instance = null;
    private int cnt;

    public synchronized void release() {
        cnt++;
        notifyAll();
    }

    public synchronized void acquire() {
        while (true) {
            if (cnt > 0) {
                cnt--;
                break;
            }
            else {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static Counter getInstance() {
        if (instance == null) {
            instance = new Counter();
        }
        return instance;
    }
}

  • 至于如何实现双轿厢电梯互斥问题,我学习了讨论区中的写法,用一个Flag的对象来进行判断。这非常地简便
public class Flag {
    enum State {
        OCCUPIED,
        UNOCCUPIED
    }

    private State state;

    public Flag() {
        this.state = State.UNOCCUPIED;
    }

    public synchronized void setOccupied() {
        waitRelease();
        state = State.OCCUPIED;
        notify();
    }

    public synchronized void setRelease() {
        this.state = State.UNOCCUPIED;
        notify();
    }

    private synchronized void waitRelease() {
        notify();
        while (state == State.OCCUPIED) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

1.3.3 调度器设计

  • 碰到双轿厢的重置信号是,我会直接在原先的电梯中启动两个双轿厢电梯线程,并让该电梯吐出乘客后结束。两个新的双轿厢则共享原先的侯乘表对象。

在这里插入图片描述

二、迭代总结

  • 由于架构的简单直接,这三次迭代我还是完成的比较顺利,期间也没有遇到多线程场景下容易发生的线程安全问题。但这种粗暴的设计也导致我损失了一定的性能分。还是挺令我感到遗憾的。

  • 但总结看来,Random大法在可拓展性极高的优势。同时,它不需要调度器知晓电梯自身的状态,避免了线程间的过多耦合以及这可能导致的线程不安全问题。在客户需求没那么严苛的情境下,这不失为一种好的选择。

三、bug与debug

  • 在第七次作业中,我由于未考虑到两个双轿厢共享侯乘表时,双方的修改都会导致另一方可能被唤醒。假设有A电梯不需要移动,但是B电梯收到了大量乘客需要移动,这就会导致A电梯被不断地唤醒,产生一定程度上的轮询。最终这导致我CTLE。不过由于这并不会轮询太多次,我在后面加了个sleep就应付了bug修复。

  • 至于多线程debug方面,轮询的问题可以通过print来找到,但其他bug我感觉就需要通过数据生成与比对来处理了,我也是并没有发现什么特别有效的方法。

四、心得体会

  • 在这三次迭代中,虽然遇到了不少困难,但我感觉还是较好地掌握了多线程编程的能力;同时,在OS的进程与线程教学中相互印证,感觉实在是收获满满。
  • 42
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值