OO第二阶段总结

设计策略总结

  1. 多线程电梯
    这次作业我主要设计了Scheduler,三个elevator,input共五个线程。input线程和Scheduler之间用一个请求队列作为共享对象连接。Scheduler和elevator一个单独的共享对象,而是将三个elevator作为Scheduler内部的对象,让Scheduler通过同步方法访问elevator的状态信息。
  2. 文件监控
    我在这次作业的设计中主要有summary,detail,file trigger,directory trigger,test,main这六类线程。
    其中summary和detail两个类用了单例设计模式,保证全局都是用一个对象,并同步写入指定的记录文本。
    file trigger和directory trigger两个类会为每个将空对象建立一个线程,使用snapshot和SafeFile类作为共享对象,snapshot类记录文件更改信息,用于前后对比。SafeFile类用于同步读写文件操作,保证文件操作线程安全。
  3. 出租车系统
    此次作业中有taxi,requestScheduler,input三个线程。其中每个taxi为一个线程,负责控制出租车在有无请求的状态下移动,input每输入一个请求,立即建立一个requestScheduler请求调度线程,这个线程负责在指定的三秒时间窗内向出租车发送请求,并在时间窗结束后选择合适的出租车分配请求。我在全局建立了一个taxi数组,桉顺序存放所有的taxi,requstScheduler可以访问这个数组并调用taxi类的同步方法得到taxi的信息,进行请求分配。

基于度量的程序结构分析

1. 多线程电梯

复杂度分析

Elevator控制运动的方法继承了前几次作业,没有重构,所以复杂度较大。
另外inputHandler类由于需要解析字符串,也嵌套了大量判断,导致环路复杂度较高。

设计缺点:

  1. 电梯控制方法比较混乱,导致复杂度高,而且调试过程很繁琐,需要重构改进。
  2. 我的程序在需要不停的循环查找、分配请求,这样会比较浪费cpu资源,需要使用对象监控器做到wait(),notify()的工作模式,减少进程空跑的时间。
  3. 请求分配,执行过程比较复杂。首先,在我的设计中,电梯作为调度器内部的变量,没有中间共享对象,这样就将二者混成一个整体,结构不清晰,类的功能实现也比较复杂。另外,电梯内部我设置了一个自调度器,自调度器接受请求还需要通过电梯对象传递,这就增加了实现的复杂度。相比如下的设计方式会更简洁。
    这些问题归根到底还是因为最初的设计不够充分,导致后续更改要不断包容之前的缺陷,导致整体越来越笨重。

2. 文件监控

复杂度分析

这一次作业复杂度比较平衡,各个类及其方法都比较均衡。

设计缺点

  1. 从类图上看,类的从属关系还是比较复杂,分析可见在触发器类中定义了detail,summary,snapshot,SafeFile这些类的对象,这让触发器的内容比较多,程序功能的实现也确实主要依赖于触发器线程。
    优点:
  2. 在DetailThread,SummaryThread,SafeFile三个类中使用了单例设计模式,并在其中使用类对象同步的方法,保证了线程安全。
  3. 在这次作业中,每个类的复杂度都不高,比较平均。

3. 出租车系统

复杂度分析

本程序主要有mapInfo类和Taxi类。
在maoInfo类中有一个方法用于计算一个节点连接的其他节点,由于map用0-3四个数字表示连接情况,所以这个方法中用了较多的判断,导致复杂度较大。
在Taxi类的Run方法中,由于Taxi需要根据当前状态以及运动,所以在里我写了一个状态机,也存在循环和判断的嵌套,使复杂度较高。

设计优缺点

缺点:

  1. Taxi运动控制方法比较混乱,而且出现了bug。
  2. Record记录比较繁琐,按照作业要求,taxi在接单后的所有运动信息都要记录,所以我定义了一个record类用于记录信息,并在每次运动后调用对应的方法,记录信息,在写程序时,很多地方都要调用record对象的方法,容易漏掉。

优点:

  1. 共享对象OrderMatrix和mapInfo用了单例设计模式,保证信息一致和线程安全。
    OrderaMatrix用于记录请求信息,并判断同质。

Bug分析

  1. 多线程电梯
    死锁导致无效。。对锁的用法理解不好。
  2. 文件监控
    未发现bug
  3. 出租车
    (1) 寻路算法出错,对已查看过的点为标记,导致重复查找,对远点路径查找会卡死。
    (2) 输入的无效请求,判断正确,但是字符串处理方法未及时return,结果定义了一个无效的请求对象。

体会与感悟

在多线程电梯作业中出现了死锁bug,导致了作业无效。这里谈一下我对锁的理解。
java中锁有几类,对象锁,类锁,读写锁。

  1. 对象锁和类锁
    得到锁的方式:synchronized关键字修饰。
    synchronized修饰非静态方法、同步代码块的synchronized (Object)用法的用法锁的是对象,线程想要执行对应同步代码,需要获得对象锁。
    synchronized修饰静态方法以及同步代码块的synchronized (类.class)用法锁的是类,线程想要执行对应同步代码,需要获得类锁。

    释放锁:在synchronized修饰的代码段结束。对于对象锁也可以在synchronized修饰部分调用该对象的wait()方法释放锁,进入等待状态。
  2. 读写锁
    读写锁分为读锁和写锁,多个读锁之间是不需要互斥的(读操作不会改变数据,如果上了锁,反而会影响效率),写锁和写锁之间需要互斥,写锁和读锁之间需要互斥。
  3. wait(),notify() 用法
    二者都必须在synchronized修饰下使用,且调用的是同步对象的方法,即同步对象等待,同步对象被唤醒。

posted on 2018-05-02 00:14  L__L  阅读(185)  评论(0)    收藏  举报