• Welcome to the world's largest Chinese hacker forum

    Welcome to the world's largest Chinese hacker forum, our forum registration is open! You can now register for technical communication with us, this is a free and open to the world of the BBS, we founded the purpose for the study of network security, please don't release business of black/grey, or on the BBS posts, to seek help hacker if violations, we will permanently frozen your IP and account, thank you for your cooperation. Hacker attack and defense cracking or network Security

    business please click here: Creation Security  From CNHACKTEAM

Recommended Posts

OOunit2总结博客

(1)总结分析三次作业中同步块的设置和锁的选择,并分析锁与同步块中处理语句之间的关系

中的作业同步块都在共享对象中,共享对象实现以下接口:

公共接口队列{

void addRequest(请求请求);//添加成员

void setEnd();//传递结束信号

布尔isEnd();//检查信号是否结束。

布尔need wait();//使用此方法让进程判断是否需要等待。

布尔型isEmpty();

}

具体实现类如下:同步块中的方法用synchronized关键字修饰。addset和addset方法在更改类中的属性时,通过notifyAll()语句唤醒同一个共享对象的等待线程。

导入com . oo course . elevator 3 . request;

导入Java . util . ArrayList;

公共类QueueReq实现Queue {

私有ArrayListRequest请求;

私有布尔isEnd

public QueueReq() {

requests=new ArrayList();

isEnd=false

}

@覆盖

公共同步void addRequest(请求request) {

requests.add(请求);

notify all();

}

@覆盖

公共同步void setEnd() {

isEnd=true

notify all();

}

@覆盖

公共同步布尔isEnd() {

返回isEnd

}

@覆盖

公共同步布尔needWait() {

if (requests.isEmpty()) {

尝试{

//output . println(thread . current thread()。getName()' wait ');

wait();

} catch (InterruptedException e) {

e . printstacktrace();

}

}

return requests . isempty();

}

@覆盖

public synchronized boolean isEmpty(){

return requests . isempty();

}

}

(2)总结分析三次作业中的调度器设计,并分析调度器如何与程序中的线程进行交互

第二单元第一次作业

第一次运行,楼层只有一部电梯,每层只有一部电梯。电梯调度员负责将相应楼层的乘客分配到相应的电梯等候名单中。

第二次作业

在第二次操作中,增加了同一栋楼的几部电梯和循环电梯,调度员负责将相应楼的乘客分配到相应电梯等待列表的列表类中,使用列表类方法将乘客请求添加到电梯等待列表中。(这部分是调度器的分配策略。为方便起见,选择基准策略进行平均分配。也就是说,当同一建筑物或楼层中的电梯等待列表中有n部电梯时,该建筑物中的乘客被平均分配到这些电梯的等待列表中)

由于第二个作业添加了添加电梯的请求,调度程序不仅负责电梯线程的结束,还添加了添加电梯的方法,并负责电梯线程生命周期的开始。

第三次作业

第三次分配在第二次分配的基础上增加了转移。因此,在第二工作功能的基础上,调度员还实现了乘客请求的拆分和冻结。分割是指调度程序维护建筑物之间的连接表。当两栋楼之间有一个环形电梯可以水平直达时,我就把它当成连通了。根据连通度,判断所请求的换乘是连通循环电梯还是一层全连通。

电梯。每当添加一个新电梯时会更新一次连通表,每当添加一个新乘客时也会查询一次连通表。当连通表维护好之后,乘客需要换乘的请求就可以进行拆分了。根据目标楼座、目标楼层、当前楼座、当前楼层、连通表这五个因素,把乘客请求拆分成两个或者三个或者一个横向直达的请求。然后把第一个请求当成第二次作业的方式直接处理,即放到候乘表列表里,不同的是,第二次作业的环形电梯是每一层有一个候乘表列表管理N个电梯,第三次作业则考虑连通情况,每一层有10个候乘表列表,代表A-B连通,A-C连通,……D-E连通,10层一共100个候乘表列表。

当新添加一个横向电梯时,举例:例如一个6层可以在ABE座开门的电梯,则它的候乘表需要添加到6层A-B连通、6层A-E连通、6层B-E连通三个候乘表列表中。如果某个乘客请求中间会走6层的A-E,该请求就会被分配6层A-E连通的候乘表列表中,就有可能会按分配策略分配到这个电梯中(依然采取平均分配策略。)

初始化该列表的部分代码如下:

private void init() {
    /*……*/
    for (int i = 0; i < 10; i++) {
        // init floList
        // init connectedTable
        connectedTable.add(i, new ArrayList<>());
        floList.add(i, new ArrayList<>());
        for (int j = 0; j < 10; j++) {
            floList.get(i).add(j, new QueueList());
        }
        // 初始横向电梯放入全连通一层。 j = 0 ,floor = 1
        floList.get(i).get(0).addQueue(ele.getSubQueue());
    }
    /*……*/
}

第三次作业中的调度设计还有一个重要的地方是拆分请求后要按顺序执行,故之后的后序请求都会加入冻结列表
private final QueueReqFreeze queueReqFreeze;

调度器的运行逻辑和冻结列表里是否有请求,是否需要唤醒密切相关。
queueReqFreeze:冻结列表
queueMain:和输入线程共享的列表,主要代表控制台输入
handleFreeze:处理冻结列表中的请求。调度器和所有电梯共享一个对象messageTray,当电梯完成一个请求,它会把乘客的id,当前走出电梯的楼层楼座输入到这个共享对象中,每当调度器要处理冻结列表中请求,判断是否要解冻的时候,就会在messageTray 中搜索是否和冻结请求的出发楼座和楼层还有乘客id一一对应。如果有则代表上一阶段的请求已经完成,需要解冻处理下一阶段请求了
handleRequest:在第二次作业的基础上,除了有已经实现的分辨请求类型、创造电梯开启线程、或者把乘客请求放入对应候乘表列表中以外。还实现了创造电梯后更新连通表、考虑乘客请求是否需要拆分和冻结等。

public void run() {
    init();
    while (true) {
        if (queueReqFreeze.isEmpty()) {
            if (queueMain.isEnd() && queueMain.isEmpty()) {
                // set All sub end
                for (int i = 0; i < 5; i++) {
                    buiList.get(i).setEnd();
                }
                for (int i = 0; i < 10; i++) {
                    for (int j = 0; j < 10; j++) {
                        floList.get(i).get(j).setEnd();
                    }
                }
                return;
            } else if (queueMain.isEmpty()) {
                if (queueMain.needWait()) {
                    continue;
                }
            }
            handleRequest();
        } else {
            if (queueMain.isEnd() && queueMain.isEmpty()) {
                if (messageTray.needWait()) {
                    continue;
                }
                handleFreeze();
            } else if (queueMain.isEmpty()) {
                handleFreeze();
                if (queueMain.needWait()) {
                    continue;
                }
            } else {
                handleFreeze();
                handleRequest();
            }
        }
    }
}

在第三次作业的调度器中,告诉电梯线程该结束的判断条件为,冷冻列表为空,输入线程不再输入且为空:

queueReqFreeze.isEmpty() && queueMain.isEnd() && queueMain.isEmpty()

而开启电梯线程同样在创造电梯线程的方法中实现:private void createEle(Request request) {}

调度器设计总结

总的来说三次作业调度器的设计是在前次作业基础上根据所需新功能不断迭代而需要实现的。从开始只需要处理乘客请求、控制电梯线程的终结;到第二次作业需要处理创造电梯的请求,以及开启电梯线程的生命;到第三次还需要拆分请求,维护连通表,冷冻请求等。根据需求进行设计。

(3)结合线程协同的架构模式(如流水线架构),分析和总结自己

三次作业架构设计的逐步变化和未来扩展能力画UML类图

  • 第一次作业UML类图较为简单:架构主要有调度器、输入线程、电梯类,主线程较为简单主要负责顶层连通,这里不展示在类图中。
    t5fbayi004w4749.jpg

  • 第二次作业添加了工厂模式和环形电梯类,调度器的属性略有改变
    1ghac1pzpf04750.jpg

  • 第三次作业架构在第二次作业基础上更改不大,主要添加了电梯类和调度器的共享对象(图中没有给出),调度器的属性更新了冻结列表(如图),添加了连通表类(为了降低耦合单独成类,可并入调度器,UML图中没有给出)
    1z44kpojlrb4751.jpg

三次作业的架构是迭代设计的,如类图所示,在不满足新作业功能要求的情况下会引入新的中间类,通过扩展中间类或者接口,然后继承实现的方式进行扩展。

  • 画UML协作图(sequence diagram)来展示线程之间的协作关系(别忘记主线程)
    n05ierxhebq4752.jpg

    三次作业除了第三次电梯会给调度器回传 messageTray信息以外,前两次都是一级一级自行判断线程结束并向下传递结束命令。(而且第三次作业电梯回传的messageTray也只是方便调度器清空冻结请求列表freezeReqQueue,当该冻结请求列表为空且其他结束条件满足后,调度器会结束自己进程,此后电梯线程写入共享对象的信息并无意义。因此,此三次作业的时序图皆可用一张图表示,即线程完成自身任务后就结束并向下传递结束命令,输入线程负责处理输入,调度器负责分配候乘表,电梯线程负责运送乘客,所有线程结束后主线程也结束)。

(4)分析自己程序的bug

公测第二单元第一次作业部分评测卡在waiting没有结果,bug修复直接提交后得到100分。

第二次、三次作业公测互测均没有bug。
分析是由于简单的设计带来更高的容错率,首先是同步块和锁的设计,同步块全部设计在共享对象的方法内,而且有意避免轮询的出现,在共享对象中加入 needwait方法,在需要获取共享对象时判断是否需要等待,如果需要,该线程立刻释放共享对象锁和CPU资源。
其次是电梯运行逻辑,详细界定了输入线程、调度器、电梯的生命周期开始结束边界判定。避免出现线程结束乘客请求还未处理或者乘客还在电梯内未能送达目的地的情况。
架构设计每个线程对自己生命周期和向下传递结束信号的设计也减少了耦合,避免复杂情况导致的bug。

(5)分析自己发现别人程序bug所采用的策略

列出自己所采取的测试策略及有效性

用测试自己程序的样例测试别人程序。由于对互测参与度低故没什么有效性。

分析自己采用了什么策略来发现线程安全相关的问题

通过构造特殊的案例,比如短时间内出现大量请求。

分析本单元的测试策略与第一单元测试策略的差异之处

多线程协作无法构造单元测试验证一些模块的准确性。

(6) 心得体会

从线程安全和层次化设计两个方面来梳理自己在本单元三次作业中获得的心得体会

  • 线程安全:越简单的设计容错率越高,同步块操作全部由共享对象完成,这样保证线程获得共享对象的锁的时候,其他线程调用共享对象的方法需要等待线程的锁的释放。
  • 层次化设计:善用设计模式,例如电梯内部类的状态模式、调度器和电梯间的工厂模式。保持低耦合,这样在迭代下一次作业的时候只需要更改一些模块间的参数,以及增添一些类和接口来当中间件实现新的功能,例如新的共享对象,新的共享对象列表。迭代调度器功能时,电梯内部运行逻辑可以几乎不变,只需要保证调度器将正确的请求分配给电梯线程,电梯类要做的修改只是参数上的变化。另外,将需要抽象的东西包装成类或者接口再继承,这样原有已实现的功能类所修改的仅仅是一些继承关系等,新实现的功能类则可以参考原有类做一些属性和方法的修改来区分开来。
Link to comment
Share on other sites