307
社区成员
发帖
与我相关
我的任务
分享hw5:全部使用synchronized修饰方法的形式。主要是针对RequestQueue的add(),pop(),isEmpty(),isEnd(),setEnd()
hw6:在RequestQueue中使用了条件锁。主要是为了区别非空唤醒和检修完毕的唤醒。
hw7:放弃lock条件锁,再次改用synchronized修饰方法和同步块。方法类同上,同步块主要是针对双轿厢的防撞和“主轿厢未经RECEIVE离开F2与备用轿厢的RECYCLE之间的顺序”
首先,锁用于保护同步块中的语句最多只被一个线程访问;
其次,锁和同步块中的数据改变没有必然的联系,锁可以只是一个简单的Object对象
Object recycleLock = new Object();//MainClass中创建的一个只用于做锁的对象
synchronized (recycleLock) {
TimableOutput.println(
String.format("RECYCLE-END-%d",elevatorId));
otherElevator.setType();
}//recycle过程
if (isYield) {
synchronized (recycleLock) {
if (type == Type.MAIN) {
nowFloorStr = FloorMapper.toString(newFloor);
TimableOutput.println(
String.format("ARRIVE-%s-%d",nowFloorStr,elevatorId));
nowFloor = newFloor;
}
}
}//主轿厢在无RECEIVE的情况下离开F2前的检查情况
最后,在同步块内部如果用到了wait,那么wait对象必须要和锁的对象保持一致。比如在防撞中通过shaftLock共享锁进行对于两个轿厢的控制时,在synchronized(shaftLock)中用的是shaftLock.wait(),shaftLock.notifyAll()。具体如下:
Object shaftLock = new Object();//MainClass
synchronized (shaftLock) {
int otherFloor = otherElevator.getFloor();
if (otherFloor == 2) {
try {
shaftLock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
TimableOutput.println(
String.format("ARRIVE-%s-%d","F2",elevatorId));
nowFloor = 2;
}//防撞处理逻辑
if (isSecFloor && this.type != Type.BOTH) {
synchronized (shaftLock) {
shaftLock.notifyAll();
}
}//move()中的,wait之后不要忘记唤醒
提取Request指定的电梯号即可,然后通过ProcessingQueue(电梯的请求队列)和电梯线程交互,通过RequestQueue(总请求队列)和输入线程交互。(不过好像确实没有必要搞两个队列)
共享对象同上,调度策略是根据哪个电梯人数最少且不在检修的话就把人分给那个电梯,如此以来,除了电梯的人数和检修状态之外我们并不需要增加分配线程和电梯线程的交互以及相关的维护。
放弃Dispatch线程,使用自由竞争的策略,可以认为电梯线程在接人的同时充当了用于分配请求的分配线程。同时由于电梯只有相应的RECEIVE才可以开始移动,所以在电梯为空的时候,需要先随机给电梯分配一个请求。
交互:
和InputThread的交互:将电梯的Map和备用轿厢的Map传入了InputThread线程中,通过InputThread直接修改Elevator的状态,并唤醒。可以通过为电梯添加一个Status的枚举变量:NORMAL,MAINTING,UPDATING,RECYCLING,然后一旦检测到不是NORMAL就直接进入相应的处理函数。
和备用轿厢的交互:通过总的请求队列、共享锁、各自电梯的楼层和方向。在总的请求队列中,我将总的请求分成了两种:一种是起始楼层大于2或者起始楼层等于2且方向向上;一种是起始楼层小于2或者其实楼层等于2且方向向下的。而后面共享锁的内容主要作用可见第一部分。
和其他轿厢的交互:通过总的请求队列和end的情况,主要是注意请求的分配和再分配以及线程结束的问题。
hw5中通过不关门来减少一次开关门导致的电量消耗(不过不太符合现实情况,在后面的策略中丢掉了)
hw6中想通过人数分配,这样的话可以让请求分配近似随机和均匀以减少时间,但是会导致在其他电梯都检修时,大量RECEIVE涌入同一个电梯,于是再次放弃
hw7中通过自由竞争分配,这样的话对于捎带请求来讲可以尽可能压缩时间,对于主请求由于是电梯抢占,可能存在一些电梯电量资源浪费的情况(比如A,B,C原本可以只由一个电梯运送,现在会启动三个电梯),但是个人感觉无伤大雅
从电梯向分配线程的再分配:在hw6中我本来设计了一系列的函数想要电梯在接收到请求的时候尽可能送乘客,以此来节省电梯开关门和上下行的电量,但是在后续增量开发时由于觉得架构过于繁琐简化为了一旦接收到临时请求则立刻放人。
类图:

协作图:

针对一些新的临时请求会有较好的可扩展性,因为自由竞争可以尽可能保证一个人一旦被踢下来就会被先到的电梯接走,所以我在临时请求一到达就把相应的乘客放出电梯。
对于输入队列的组织方面,听过研讨课的分享之后,感觉自己的数据结构并不是特别的好,将总队列以F2为边界进行分流的话没有办法应对不同电梯换成楼层不一样的情况,不如给原始的请求专门加一个变量记录中转层。
是由于大量RECEIVE涌入同一电梯导致的总时间超时
由于某些原因导致的CPU超时
大量复杂输出中的WrongAnswer问题
debug:首先通过命令行先把特定电梯的输出集中到一个文件中,便于观察
#!/bin/bash
# run.sh
TARGET=${1:-2} # 默认值为2
awk -F- "\$NF==$TARGET {print}" stout.txt > stout_elevator.txt
awk -F- "\$NF==$TARGET {print}" stdin.txt > stdin_elevator.txt
echo "生成完成,筛选目标值: $TARGET"
线程在完成请求之后并没有自动结束
互测时的bug寻找:
F2的防撞
主轿厢在未经RECEIVE时的移动与RECYCLE结束之间的前后顺序
某个电梯一次性涌入大量RECEIVE的情况
主要使用的大模型:chatgpt,DeepSeek
利用Deepseek进行关于锁和同步块的知识的巩固和回顾
利用chatgpt进行debug和加锁策略
大模型在知识方面和代码简化方面表现优异,但是在debug和实现细节方面会显得冗余,具体表现如下:
需要跟大模型明确题目中的重点,不然大模型会给出很多不存在的bug(幻觉问题)
在我的防撞加共享锁时,大模型给了我一个很繁琐的新类,但其实只需要加一个Object即可
但是大模型对于输出次序的把握和线程安全方面的把握很好
感觉关键还是自己要把握指导书的要点然后向大模型进行详细和完整的说明
在U2中进行了2次重构,感觉个人对于重构的耐心度和接受度提高了一点,但是在U2中对于电梯策略的了解不够,导致优化时把握错了重点,把重心放在了检修时尽可能送客而不是一开始的分配,导致代码架构很丑陋,其实在自由竞争的前提下,早一点将人放出也有助于请求早日结束。
在互测的时候有时候会忘记之前自己的hack数据,如果可以的话希望可以开放自己的互测数据的记录。