🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
北航OO第二单元(多线程实时电梯系统)总结
本单元的总体任务是维护一个ABCDE共5楼座、10层的目标选择电梯系统。需要接受乘客的请求和增加电梯的请求,并运行相应电梯将乘客送达目的地。输出所有的电梯运行以及上下人行为。
一、同步块和锁
第五次作业中只有竖向电梯,每座各有一个,均可达1-10层,乘客请求出发地和目的地楼座一定相同。
1、第五次作业
由于只有五部电梯,并且所有请求对应的电梯是明确的,所以没有设置全局调度器,而是选择由输入线程与所有电梯共享一个候乘表对象,电梯直接从候乘表中取出自己对应的请求。为了简化其他类的设计,这里直接将候乘表设置为线程安全类,同步块具体代码如下:
public synchronized void addRequest(PersonRequest request) {
char fromBuilding = request.getFromBuilding();
this.requests.get(fromBuilding).add(request);
notifyAll();
}
public synchronized PersonRequest getMainRequest(char nowBuilding) {
while (requests.get(nowBuilding).isEmpty() && (!close)) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (requests.get(nowBuilding).isEmpty()) {
return null;
}
return requests.get(nowBuilding).get(0);
}
public synchronized ArrayList getForm(char nowBuilding) {
return requests.get(nowBuilding);
}
public synchronized PersonRequest getNowFloor(char nowBuilding, int nowFloor) {
ArrayList list = requests.get(nowBuilding);
for (PersonRequest personRequest : list) {
if (personRequest.getFromFloor() == nowFloor) {
list.remove(personRequest);
return personRequest;
}
}
return null;
}
public synchronized void close() {
close = true;
notifyAll();
}
public synchronized boolean checkOver(char nowBuilding) {
if (!close) {
return false;
} else {
return requests.get(nowBuilding).isEmpty();
}
}
public synchronized void output(String in) {
TimableOutput.println(in);
}
可以看出,添加请求、电梯空时获取请求getMainRequest,策略类获取当前楼座候乘表getForm,电梯上人getNowFloor,输入结束close,检查是否结束线程的checkOver和输出output都在同步块内。在某电梯线程或输入线程访问时均会阻塞其他线程的访问,以此保证线程安全。特别的,当电梯无人且主请求为空时,会执行wait()等待,输入线程每当进行输入之后会执行notifyAll()唤醒所有空置等待的电梯,以此可以避免电梯空时轮询占用CPU资源的问题。
PS:现在第二单元结束之后需再看这段代码其实有一些问题,也有可以优化的地方。
- 首先是getForm很有问题,它直接将该共享对象的一部分的指针(?)暴露给了同步块之外的部分,电梯的策略类使用的时候如果输入线程对其进行了修改,很容易出现RunTimeError,不过强测居然全都过了现在想想也是幸运。
- 之后是方法冗余的问题,checkOver函数完全可以综合进getMainRequest中,比如使用null作为返回值标记线程结束,而不需要单独写一个方法。
2、第六次作业
第六次作业在第五次作业的基础上增加了横向电梯,横向电梯可以到达相应层的所有座。乘客请求在第五次作业的基础上可以发出出发和目的地相同层不同座的请求,且请求保证在该层添加电梯1s后(保证了电梯一定先被完成添加)。由于出现了新增电梯的请求,在输入线程设置了了两个共享对象,一个乘客请求总队列ScheduleQueue负责存储所有输入的乘客请求,一个电梯增加请求队列ElevatorAddingQueue负责存储所有添加电梯的请求。同时每楼座的纵向电梯和每层的横向电梯共享一个候乘表,由调度器进行分配。与上一次思路相同,为了减轻线程类的思维量,这里直接将所有的共享对象设置为线程安全类。
ScheduleQueue同步块代码如下
public synchronized void addRequest(PersonRequest personRequest) {
waitingRequests.add(personRequest);
notifyAll();
}
public synchronized PersonRequest getRequest() {
while (waitingRequests.isEmpty()) {
if (end) {
return null;
}
try