BUAAOO——第二单元总结

一.总结分析同步块的设置和锁的选择

第一次作业:

  

  第一次作业类图如上,主线程创造出Provider线程和Cnotroller线程后就结束了,WaitPeople类就是等待队列,也就是两个线程的唯一共享数据,第一次作业只有一部电梯,所以我没有设置调度器,所以共享资源的控制十分简单,我的同步块基本上都是WaitPeople的同步方法,因为只有它一个共享资源,所以使用同步方法,以这个共享资源自己为锁,可以很方便并且很安全的控制线程。同步方法都有判断是否有人可以上电梯,把人从等待队列删除,把人加入等待队列,大部分同步方法都对等待队列有写操作。本次的同步代码块只有两处,一是Provider线程结束时,唤醒等待WaitPeople锁的线程,二是Controller线程判断是否没有资源可以获得时,以wait进入阻塞状态,本次作业的锁均为同一个WaitPeople对象。

第二次作业:

  

  第二次作业增加了多部电梯所以相比于第一次作业,我增加了调度器类,并且给每个电梯一个单独的等待队列(WaitPeople),第一次作业Provider线程直接往唯一的WaiPeople对象中传入请求,这次由于有多个等待队列,所以Provider的行为改变为向调度器Scheduling中传入请求,调度器中包含了所有的等待队列,由调度器来决定请求加入到哪个等待队列,这次作业在同步块的代码上和上次作业没有什么区别,都是在WaitPeople中使用同步方法,以对象本身作为锁,但和上次不同的是,上次只有一个等待队列,所以所有的锁其实都是同一个对象,而这一次,每个不同的电梯都是以一个不同的对象为锁。这个不同体现在代码上的修改在于,原来在Provider结束时会唤醒等待唯一锁WaitPeople的线程,这一次Provider结束时执行调度器中我定义好的一个方法,将所有等待队列设置为结束状态,依次唤醒被阻塞的线程。由于调度器只会被生产者使用,使用调度器上不需要加锁。

第三次作业:

  

  第三次作业和第二次作业对比没有增加新的类,但是由于这次作业增加了换乘,所以电梯中的人也有可能重新回到等待队列,由于我给每个电梯一个独立的等待队列,所以电梯中的人想要换乘只能通过调度器进入其他电梯的等待队列,因此调度器也成为了共享资源,需要加锁,我依旧选择同步方法来给调度器加锁,相比于上次作业,往调度器中送入请求也成为了同步块,以调度器对象自身为锁,并且在这个同步块中,还需要调用等待队列的同步方法,才能往等待队列中加入请求,这是和第二次作业对比,最大的区别。并且由于换乘,并且给每个电梯单独等待区间的缘故,消费者线程的结束,不能再以生产者线程结束,等待队列为空,电梯里无人来判断,我只能在调度器中加入一个容器,记录是否还有人需要乘坐电梯,对这个容器的操作也是通过同步方法,这个同步方法只需要调度器锁即可。

 二.总结分析调度器设计

第一次作业:

  第一次作业由于只有一个电梯,所以我没有设置调度器,直接让这个电梯以类似ALS的方法去运算乘客,没有去优化电梯的调度算法,导致了性能分极低。

第二次作业:

  第二次作业出现了多部电梯,我增加了调度器类,并且为每个电梯分配了一个等待队列,调度器的作用就是把请求合理的分配给某个电梯的等待队列,因为每个电梯都有独立的等待队列,并且我直接按数量平均分配了所有的请求(不管电梯的运行状况),所以消费者线程和调度器基本上是毫无关系,唯一的关系就是我的等待队列中的内容是由你添加的,调度器只与生产者线程有交互,生产者线程将请求放入调度器,由调度器分配给等待队列,因为调度器和消费者线程不交互,所以它连共享资源都不是,甚至不用加锁就是线程安全的。

第三次作业:

  第三次作业支持了换乘,我的请求想要进入等待队列就必须要通过调度器,换乘的人又一定是从消费者线程中出现的,所以我的调度器和消费者线程也有了交互,在一位乘客需要换乘时,消费者线程会将请求的进行修改(改变请求来自哪个楼层),然后将其放入调度器,调度器分配这个新请求给某个电梯的等待队列。生产者线程相比第二次作业增加了和调度器的交互,每一个从生产者线程加入调度器的请求都会被记录在容器中,当这个请求在消费者线程中到达了目标楼层后,消费者线程与调度器交互,将请求从容器中删除,以这个容器是否为空来判断消费者线程是否该结束。这次有三种电梯:A类可以到达全楼层,但是速度慢,B类可以到奇数层,速度居中,C类可以在低高层运送,速度快,我的调度中,所有低层到高层的请求都由C类电梯运送,较低楼层但不在C电梯的范围的请求由A电梯运到3层后由C电梯运算,高层也是类似的,所有奇数到奇数的请求由B类电梯运送,偶数到偶数的请求由A类电梯运送,奇数到偶数(偶数到奇数)的请求,如果楼层数够多就换乘。

三.第三次作业分析

主线程:

  首先是主线程,它创建并且启动了输入线程和初始的三个电梯控制线程,调度器也是由主线程创建,通过addwaitpeople方法增加调度器中的等待队列,在进行完这些操作之后主线程就结束了。

生产者线程:

  输入线程可以接收请求,请求有两种可能,一是乘客请求,它直接通过调度器的addperson方法加入到某个电梯的等待队列中去;二是增加电梯的请求,它创建好电梯,电梯的等待队列,电梯的控制器,把电梯的等待队列加入到调度器后启动这个控制器线程。

消费者线程:

  电梯线程只从其对应的等待队列中取得请求,在请求到达目标层数时,判断是否需要换乘,需要时将乘客放入调度器中调度,不需要时记录下这个乘客到达目的地。

分析:

  由于给每一个电梯单独的等待队列,并且在分配的时候不关注电梯本身的运行情况,所以性能上并不是很理想,但是在功能上是正确的,架构上,每个线程只关注于自己,所以在这三次作业中,扩展时基本不用改已经写好的代码,可扩展性较强。

四.分析自己程序的bug

  这三次作业在强测中都没有出现bug,但是在第二次作业的互测中被找到了一个bug,在第一次作业中我基本采用ALS的调度策略,所以电梯会有主请求,但是它的性能分是真的低,所以在第二次作业中,我修改了调度方法,但是我保留了主请求这个设定,并且在主请求目标楼层到达时,按第一次作业的程序,电梯门一定已经打开了,我只要把主请求送出电梯后关门就可以了,我修改了调度方法后,把主请求改成了电梯中的某个人,但是我忘记在一个地方设置主请求了,导致主请求可能是不在电梯中,这就导致了电梯可能会在某个楼层莫名其妙的关门,这个bug也印证了只要不优化就不会有bug。因为写程序时考虑的挺周全的,没有遇到死锁的问题。

五.分析自己发现别人程序bug所采用的策略

  这次作业我觉得不容易出现逻辑上的错误,大部分错误都是多线程导致的死锁问题,这种问题也不容易复现出来,我就没怎么hack别人。

六.心得体会

  这三次作业中,前两次作业我都不会出现同时需要两把锁的情况,还把共享资源的方法基本都设置成了同步方法,所以没有线程安全问题,第三次作业中虽然有需要两把锁的情况,但是它们的顺序是不会出现死锁的,对于这单元的线程安全,我只能说把共享资源的方法都设置成同步方法,线程基本就安全了,只要在消费者线程wait和notifyall时要注意下就行。

  相比于第一单元,这一单元的难度确实没那么高,只有第一次作业因为第一次接触多线程耗去了很多时间,后面两次作业,因为我基本不优化,所以只是在第一次作业上稍微改动了一些就完成了,花费的时间很少,并且这单元的作业,我没有重构,感觉逐渐有了一点面向对象的思想,这一单元最大的不足在于第一次作业,因为上一个单元最后难度很大,所以我直接不优化了,我把不优化的思想保持到了这单元的作业,这就导致了我第一次作业性能分极低,第二次作业迷途知反还改出了个bug,真是让我大受打击啊,但是总的来说,这一单元我还是收获颇丰。

 

posted @ 2021-04-24 14:11  zty1469  阅读(64)  评论(0)    收藏  举报