oo第二单元总结

oo第二单元总结

1.程序分析

homework_4

本单元第一次作业要求实现一个单个电梯调度系统。这次作业难度其实并不大,但由于是第一次接触多线程编程,对很多地方都出现了问题(主要是wait方法与notifyAll方法当时还不是很理解)。

整体构思是一个最简单的生产者—消费者模型,这里除了主类之外设置三个类,Producer类利用课程组提供的输入接口来负责接收源源不断的乘客需求。再将这些需求放到RequestList类里,这里采用了一个ArrayList作为可伸缩容器。之后再利用elevator线程来消化需求队列。

关于电梯调度算法采用了最无脑的scan算法,即电梯始终上下扫描运动,遇到有乘客且方向相同就接上。

homework_5

第二次作业增加了多个电梯,在最初的设计中,我添加了一个调度器线程,通过调度器来向不同的电梯分配任务,但在评测中一直出现cpu_time_limit_exceed错误,由于本人是一条懒狗,出现这个错误之后留给自己的时间不多了,干脆破釜沉舟重写了作业。将调度器线程删除,直接让电梯去竞争Request。毫无疑问,这种做法牺牲了大量性能,但也是无奈之举,最终在中测关闭前一小时通过了测试,真是有惊无险。这样修改下来,对比第一次作业基本没有什么区别,还是四个类,处理问题也大同小异。

后续通过和大佬交流认识到了自己很多地方上锁是多余的,可能会产生大量线程白白等待问题,这可能就是产生ctle的原因。

homework_6

第三次作业还是多电梯调度问题,涉及到换乘问题,我在第二次的架构上,在RequestList类中添加了分配策略,把乘客的Request进行判断,如果能够直接实现则不做改变,否则将其切分成可以实现的两个部分,前一部分实现后再唤醒后一部分request。这样每次电梯得到的就是可以实现的request。

2.可扩展性分析

SOLID原则自我评价

  • SRP:单一职责方面,觉得这三次作业都做得不错。其中对于第三次作业的换乘任务应该新建一个换乘类来实现而不是放在RequestList里,这里没有做好。
  • OCP:关于开闭原则,elevator类中的许多内容会被修改,没有很好遵循。
  • LSP:本次作业我并没有使用继承关系来解决问题,所以这个准则在本次作业中没有什么体现。
  • LD:关于类之间的相互调用,参照UML图,后两次作业有一些位置处理的不好,正常来说只涉及到elevator到RequestList,Producer到RequestList的相互调用,但我为了处理电梯线程的关闭,又添加了电梯对Producer是否关闭这一状态的调用。这样一来一定程度的违背了LD原则。
  • ISP:第三次作业实际上应该使用接口来实现三种电梯,但我都把它们定义成类的属性,再通过构造方法来实现不同的电梯,这样一来构造方法就十分冗杂。(面向对象思维还是很欠缺啊!)
  • DIP:本次作业没有涉及到一些顶层模块与底层模块的一些依赖关系的问题,所以DIP准则在三次作业中也没有什么体现。

3.度量分析

第四次作业复杂度:

oo第二单元总结

第四次作业UML图:

oo第二单元总结

第五次作业复杂度:

oo第二单元总结

第五次作业UML图:

oo第二单元总结

第六次作业复杂度:

oo第二单元总结

第六次作业UML图:

oo第二单元总结

4.bug分析

首先是自己编码中遇到的问题,关于List以及Map容器在遍历删除过程中,如果直接暴力Remove则会触发异常:

java.util.ConcurrentModificationException。

解决方案是借助Iterator来帮助实现,例如我在实现人员出的时候涉及到删除Map中元素:

for (Iterator<Integer> iterator =
     personList.keySet().iterator(); iterator.hasNext(); ) {
    int id = iterator.next();
    if (personList.get(id) == floor) {
        TimableOutput.println(
                String.format("OUT-%d-%d-%s", id, floor, name));
        rl.trans(id);
        iterator.remove();
        inNum--;
    }
}

这里借助Iterator的remove方法来实现。

第一次作业强测错了两个点,都是RTLE的问题。后来de出来发现是电梯线程没有及时关闭的情况

 synchronized (rl) {
                if (rl.getFlag() && personList.isEmpty() && rl.isEmpty()) { 
            //进程结束
                    break;
                }
                while (rl.isEmpty() && personList.isEmpty()) {
                    try {
                        rl.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

这里如果再rl.wait处终止输入而不做其他处理,那么电梯线程将会一直在这里wait,处理方法是在输入器关闭之后notify还在wait的电梯线程,这样电梯就能顺利到if判断语句,退出线程。

后面两次作业强侧都没有错误,互测也没有被刀中。

5.总结分析

本单元主要学习了多线程的相关知识,回顾这一单元的学习,感觉自己对新知识的理解很慢。很多程序片段都是照猫画虎写出来的,而对其真正含义理解不深刻。到了第三次作业才算是真正理解了多线程的一些基本操作的真正内涵(可能与在隔壁OS又学了一遍多线程有关)。

但目前为止,自己除了使用synchronized关键字,wait、notify方法,sleep方法以外的多线程方法还是一无所知;此外,对于多线程程序的调试,还是习惯暴力输出调试法,对于idea提供的在当Thread模式下调试多线程的方法掌握的还是不够熟练,今后还要继续练习。

上一篇:[CF95D]Horse Races


下一篇:1221. 分割平衡字符串