多线程的协同与同步控制
第五次作业
作为第一次多线程作业,对于多线程的同步和协同机制还不是非常了解。在这次作业中,我将三个电梯作为了三个线程,输入线程和调度器线程进行辅助。输入线程和调度器线程之间采取了很简单的生产者消费者模式来进行指令的传递。比较重要的是调度器与三个电梯之间的协同,共享资源为指令与电梯的状态。由于要考虑电梯的状态进行分配指令,电梯状态的同步是十分重要的,我在电梯类中设计了一个专门的方法返回电梯状态并将这个方法synchronized来保证调度器获得的电梯状态与实际电梯状态的同步。
第六次作业
这次作业的共享资源主要是目录下的文件,对目录下文件的检测与对目录的下文件的操作要同时进行。很重要的一个同步措施是设计一个线程安全的文件类来保证文件被改动时的同步性。我个人在这次设计中出现了一些问题。第一个就是线程的建立,我对于每一条监视指令,建立一条监视线程,然后这个监视线程不断地扫描监视目录下的文件,获得快照。好处是看起来没啥问题加开的线程比较少,不过实际处理中问题太多,最关键的一点是无法保证文件被操作的同步性。即使我将对文件的操作上锁,但是由于相同的文件(路径相同),在两个线程中建立的file索引是不同的,实际上完全无法保证线程安全。
第七次作业
这次作业是关于100辆出租车的叫车问题。我个人认为这次线程安全的设计,和三部电梯并没有本质不同。不同在于调度策略以及更加合理的代码设计。关键依旧是对于出租车状态这个共享资源的控制,需要保证出租车的状态被锁住。对于每个请求,我不喜欢每个都开一个线程导致线程过多,我的做法是把它们放在调度器线程中,不断扫描,经过3s后自动清除。
基于度量对程序的分析
第五次作业
以上为本次作业的代码分析,可以看到McCabe圈复杂度依然是红的,本身的方差也不算太小,但是与上一次作业相比,已经有了很大的改进。改进主要来源于对输入控制,调度和电梯类的分离。最大深度还算可以,最大的深度在scheduler类的run方法,有6层。
第六次作业
这次作业复杂度整体和上次相似,迭代深度略深,可能是由于我采用了递归的方式来检查目录下的文件。
这是本次作业的类图,可以看出,这次设计比较无脑,没有仔细地规划。只是单纯地对于每个目录建立监视线程。
第七次作业
与前两次作业相比,无论是代码行数,类数,方法数都有所上升。值得注意的是,圈复杂度的方差有点过大
究其原因,Taxi类中的findNextPlace方法值得注意,这个方法是用来帮助出租车寻找下一个要行驶的地点的。由于掺杂了多种情况,甚至包括了BFS,导致复杂度非常大。下次作业考虑重写该方法,拆分其功能,降低复杂度。
从类图可以看出,这次作业和第五次相似,100个taxi线程同时运行,shceduler负责对他们的调配
分析自己的Bug
第五次作业
bug1:在INVALID情况下也去掉括号了,应该原版输出输入的内容
bug2:一行中指令用;隔开,对于所给输入,“;;”之间无指令应予以报错反馈,我直接当做不存在指令没有报错
第六次作业
bug1:没有忽略相同的请求
bug2:没有考虑在指令超过100条时如何处理,在超过100条时我直接选择忽略后面的指令。然而100条之内的指令有可能是无效的,所以在一次性输入过多指令时会产生Bug。
bug3:递归时记录文件出现了错误,导致对文件的记录比实际上的要少。
bug4:未准确理解指导书中关于modified的定义,原来误以为modified是只有最后修改时间变化。
第七次作业
暂时未被发现bug
发现别人Bug的策略
检查主要是对于测试树上节点的检查,步骤为:
基础功能检查---查看代码---分析共享资源---对于共享资源构造测试用例,检查线程安全---压力测试
由于为多个线程同时运行的程序,在压力测试和考察共享资源管理上,比较容易出现bug,原因是线程在压力测试时开的比较多,以及共享资源导致线程不安全。
自己的分析、总结与思考
心得体会
设计原则
(1)保证类,方法的均衡性。
出现god类之类的东西很容易有bug发生。
(2)集中化数据管理。
很多时候我会将一组单独写成一个类便于管理,例如电梯的状态。
(3)在底层类实现方法,提供接口,顶层类直接调用,让自己的代码更有层次感、
线程安全
我的分析思路是一开始先不考虑怎样保证线程安全。首先进行类与方法的设计,然后考虑这之间的交互,找出共享资源。最后,分析如何进行一些锁,同步措施来保证线程安全。不过有些线程安全设计不是很好,单纯的用sychronized有时候会降低并发的效率。以后应该多尝试一些书上看到的更优秀的线程安全设计。