面向对象程序设计第二单元作业总结

 

OO第二单元单元总结

一、线程的互斥处理

在多线程的学习中,我们会发现,多线程程序中的各个线程都是自由运行的,所以如果不加入线程的互斥处理,有的时候可能多个线程同时操作同一个实例。这样就会引发问题,这样的问题最早出现在我的作业二中,具体的现象是,有一个乘客同时上了两个不同的电梯,被“一分为二”。

为了解决这样的问题,我使用的方法就是给线程加锁,锁是用来保证线程安全的,对于共享对象,我们需要在其被使用时对其加锁,保证同一时间仅能对同一共享对象进行操作。

其中,需要加锁的部分在于我们对乘客信息的添加与读取,在我们读入一个新需求并把其加到等待序列的时候,以及从等待序列中取出需求,加入到电梯的线程中时,都需要加锁。在声明方法时,在前面加上关键字

synchronized,那么就能够让我们的方法在某一时刻只能由一个线程运行。例如,我的代码中,实现从等待队列中取出一个需求的方法如下:

    public synchronized MyRequest getOneRequest() {
       if (myRequests.isEmpty() && (!isEnd | count != 0)) {
           try {
               wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       if (myRequests.isEmpty()) {
           return null;
      }
       MyRequest myRequest = myRequests.get(0);
       myRequests.remove(0);
       notifyAll();
       return myRequest;
  }

在我们的作业中,因为所有的取出,加入操作都与“等待队列”这样一个抽象类有关系,于是在我的代码中,也仅对于此类进行了加锁。

二,线程的协作

除了对于线程访问同一个数据的限制之外,我们也应该对线程的状况进行更加精确的控制,比如当我的等待队列中没有某一个电梯所需要的需求,那么我应该让这个电梯线程进入等待状态,而不是重复查询需求——也就是我们常说的轮询。

此外,电梯处于等待状态不会自动返回,当我们有新的需求加入到队列中,我们应该重新激活电梯,让电梯获取到可加入的需求。当所有的需求都已经满足,并且输入已经结束,我们应该让线程也随之结束。

要满足如上的几个问题,就要用到控制线程的wait , motifyall 等方法。

    public synchronized MyRequest getOneRequest() {
       if (myRequests.isEmpty() && (!isEnd | count != 0)) {
           try {
               wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       if (myRequests.isEmpty()) {
           return null;
      }
       MyRequest myRequest = myRequests.get(0);
       myRequests.remove(0);
       notifyAll();
       return myRequest;
  }

还是同样的获取需求的方法,在这里我们则主要关注其中的 try - catch 方法,这个方法的主要

作用在于:当我们获取一个新的需求,而我们的队列中没有可加入的需求时,让此请求队列等待。

    public synchronized void addRequest(MyRequest myRequest,boolean isFirst) {
       myRequests.add(myRequest);
       if (isFirst) {
           count++;
      }
       notifyAll();
  }

与之对应的就是加入一个需求的方法,在我们新加入一个需求的时候,使用notifyall方法唤醒等待的线程,这样就实现了在没有需求的时候等待,获得新的需求的时候唤醒,重新处理需求。

那是如何判断已经结束的呢?我采用了一种计数的方法,对于每一个需求,如果是第一次加入到等待队列中,那么让count++,在完成此需求后,令count--。每次判断的时候,需要检查我的count是否等于0。此外,当输入线程结束输入的时候,会设置等待序列的isEnd属性,当count为0并且isEnd设置为真,那么我的等待序列已经结束,需要设置其它电梯内部的等待序列的isEnd属性,那么这个问题就递归到了每个电梯内部的等待序列的结束处理。之后,只需要和之前一样处理就可以——需要满足所有的电梯等待序列均为空。

三,线程的协同框架

第一次作业

第二次作业

第三次作业

 

四,Bug修复

第一次作业中,设置错误电梯人数,错误设置为7,导致强测挂掉,修复后无问题。

第二三次作业中,在强测时没有出现问题,但是互测暴露了问题:电梯的调度策略考虑不全面,导致有部分乘客需要纵向电梯换乘,但是无法分配正确的电梯,此外出现的RTLE错误在于线程之间的协作考虑不全面。

五,hack情况

第一次作业,由于是c房,提交的简单数据也能hack到不少的人。此后提交的数据主要卡了其它同学的线程和调度问题,只发现了一个同学有调度上的bug。

六,心得体会

在此单元作业前,多线程一直是一个比较模糊的概念(做完了也是)。不过这次的课上代码提供了一个很好的思路,让我能够通过样例代码,以及自己的学习,完成这一单元的作业。可以说我能够大概理解多线程的实现,也是多亏了样例的代码。在这一单元,我对于课下的自我评测并不是很熟悉,除了一些简单和自己手造的数据外,并没有做很多的测试,这也是本单元我较为欠缺的地方。

posted @ 2022-05-04 00:28  Avaleph  阅读(34)  评论(1编辑  收藏  举报