写在最前面:
转眼间就又到了一月一次的总结时间,这次的三个作业,我个人感觉可能是最令人难受的三次作业了。不只是因为它们是多线程,更是因为它们几乎是全新的三次作业,每次的代码几乎都要重头开始。
第五次作业:
复杂度分析:
首先来看看类的复杂度:
可以看出我的Elevator电梯类和Get输入线程类和Many_Schedule多线程调度类都是复杂度偏高,下面我再截取里面的方法复杂度一探究竟:
我回头仔细看一下我的代码,以我目前来看我觉得只有一个地方写的不太好,就是我把电梯类的判断运动方向,上下楼,等待,输出等都写到了run()方法中,这个地方的确是有些不妥。其他的地方例如getstring()这样的获取输入的方法,我觉得复杂度高一点也没事吧.....毕竟你需要利用正则表达式一步步判断,然后输出提示等,需要判断的条件多了,复杂度自然会比较高。
类图:
协作图:
关于BUG:
这次作业我拿到的那份作业好像是没有bug(时间有点久,记不太清了),我记得我的作业是有两个bug,一个是输入的时候我用split()分割出现了问题(其实是一个比较简单的bug,是我对split方法不熟悉导致,在此也就不过多说明);另外一个bug我想着重说明一下,就是关于时间误差的bug。这个bug从多线程第一次作业到第三次出租车作业一直伴随着我,我感觉这个bug我还会一直伴随着我走下去。其实这个问题就是用“真时间”还是“假时间”的问题。我一直采用的是真时间,也就是输出系统时间。那么问题就来了,程序在运行以及线程之间的切换,总是需要花费时间的,那么就会导致时间的误差。例如我sleep100ms,但是由于线程切换等种种原因,可能真正sleep了110ms,然后这个误差就会一直累积下去。这样最后的输出就产生了误差。很多同学采用了“假时间”,也就是不输出系统时间而是计算出时间然后输出,我个人认为这虽然可以让你的结果是正确的,但总给人感觉有点不对劲。其实就我个人的想法,这种情况在现实生活中的应用的程序可能这个误差是无伤大雅的,因为生活中的大多数情况不会以ms为单位,即使有也不会以输出时间的误差来评判对错的。
关于设计:
在这次的作业中,我认为我有两个方面的设计还是比较好的。1)我给每个电梯单独设置了请求槽,调度器只需扫描总的请求槽,然后将合适的请求推送给每个电梯的请求槽,电梯每次只需从自己的请求槽中拿出请求执行就可以了。2)在判断同质的请求时,我模拟了生活中的电梯灯。当某个请求发送成功后,相应的灯就会亮起,到时候只需判断灯是否亮就可以过滤掉同质请求了,十分简便。
关于线程安全,惭愧的讲,写这次作业的时候我还不知道怎么弄线程安全,并且以我当时的愚见来看我觉得我这次不需要线程安全,因为我对于共享资源的读以及写,都只有一个对象进行操作,我觉得好像是没什么必要让它更加安全。唯一一点可能出现问题的是可能出现对于一个队列同时读和写,但是我感觉及时同时读和写也没啥问题。
第六次作业:
复杂度分析:
首先来看看类的复杂度:
可以看出这次类的复杂度比上次作业有了很大进步,下面详细看一下具体方法的复杂度:
Get类里的读取输入的方法复杂度高就不说了,读取输入复杂度我每次都高,我认为无伤大雅。下面的复杂度高的还有几个就是监督器的四种监督类型的方法。我回头看了一下其实觉得写的其实已经很好了......以我目前的水平可能已经无法继续改进了,所以总的来说这次作业我写的十分满意。
类图:
协作图:
关于BUG:
这次我给别人测的时候,主要发现了这么几个问题:1)还是输入鲁棒性的问题,其实还是很简单的,只是他可能漏掉了。2)他的线程启动顺序可能没有安排好。我如果不进行sleep在测试线程的一开始就执行文件操作,他的监控器就无法监控到了。
关于我的bug,我只想说:我觉得我的程序没有啥bug。我举几个我被发现bug的例子:1)我在看第一版指导书的时候,上面写的是不超过10个线程,我认为就是一条请求开一个线程嘛。然后第二版指导书改成了要同时支持10个监控对象。我还以为是一个请求开一个线程,支持开10个线程,没想到原来是这个监控对象上做文章了,要最多支持40个线程。2)我的程序监控的是文件的变化,如果监控范围是目录的话实质上也是监控的具体文件的变化,这一点我可能没有在Readme里详细说清楚,造成了误解。(总的来说我觉得我不存在设计上的bug,被找到的bug只能说是语言沟通上的bug,其实被发现几个bug我都无所谓,我自己觉得我写了一个自己很满意的程序,自己感到很开心这样就够了!)
关于设计:
这次作业我同样觉得我有两个方面的亮点:
1)在进行屏幕快照时,我利用了Hashmap,利用文件的绝对路径作为哈希值,快速找出两次屏幕快照中的不同文件(这个方法在出租车中也用到了,我觉得这个方法对于快速找不同十分实用)
2)关于线程安全的设计。我认为在此次作业中,唯一可能出现安全问题的是在进行屏幕快照的过程中不能执行文件的修改操作,只需控制了这个其他应该不存在问题了(我认为同时读文件不会造成安全问题,只是时间先后问题罢了)。下面我给出我设计的思路:
首先构造一个控制类Global:
public class Global{ private int flag; public Global{ flag=0; } synchronized public boolean fileask() { if(busy==0) { busy=1; return true; } return false; } synchronized public void filend() { busy=0; } }
然后在文件读写前后加入以下代码:
while(!global.fileask()){ ; } snapshot();// or modified file// global.filend();
这样就控制了snapshot和文件修改不会同时进行,保证了线程安全。
第七次作业:
复杂度分析:
首先给出类的复杂度:
总的来说还算不错,除去gui类以外有三个类复杂度比较高,其中PassengerThread是输入线程,也就是我每次复杂度都高的那个,我就不说了。接下来截取了具体的几个方法来看:
我觉得这两个方法是我认为写的不是很好的,第一个是GrabWindow,也就是抢单窗口线程,我为每一个乘客请求都会开一个抢单窗口,但是不好的地方是我把抢单窗口要做的所有事情都写在run方法里了。还有Taxi出租车线程,我采用的类似有限状态机的原理,将状态转移以及要做的事也都写在了run方法里,回头看来这样也不是很好。
类图:
协作图:
关于BUG:
在这次作业中,我发现别人的bug只有一个就是他在进行输出的时候没有输出抢单窗口结束时刻的车辆信息,而是输出了抢单时刻的车辆信息,应该是没有仔细阅读指导书导致的吧。
关于我的bug,我这次被发现了一个bug,和我在第五次作业中写的一样,还是时间误差的问题。我这里就不详细说了,下一次作业怎么都得使用假时间了。
关于设计:
这次作业我还是认为我有两个比较好的设计,其实和上一次作业的两个亮点设计是一样的。
1)关于出租车派单安全问题。由于一辆出租车可能参与多个订单的抢单,所以在分配订单的时候,很可能出现多个订单分配给了同一辆车,这就需要我们进行控制。我采用的方法和第六次作业给出的代码一样,我控制了每次只有一个订单可以进行派单操作。
2)关于抢单,我仍然利用了Hashmap。由于抢单窗口的时间是3s,所以就需要不断扫描发出请求的区域,看是否有新的车进来,这时候利用Hashmap,以id作为哈希值,就很容易找到新进入的车了。
心得体会:
OO课过了一半了,上一周可能是最艰难的一周,经历了“周三电工实习焊一天板子”+“周三晚上OOddl”+“周三晚上被拉取上博雅”+“周四上午操作系统理论考试”+“周四操作系统实验上机测试”的恐怖一周,后面的日子可能会好过一些了。其实对于OO,越写越觉得,其实这个分呀,bug呀,从斤斤计较变得没那么重要了。我觉得能让我感到开心的是每一次完成作业,能有一个自己很满意的设计,并且写出了自己很满意的代码,这样或许就足够了。最后希望,接下来的作业能够继续加油,坚持就会有收获。