301
社区成员
发帖
与我相关
我的任务
分享

深入贯彻落实 "今朝有酒今朝醉" 的原则,在博览博客之后,发现都说去年LOOK+自由竞争是活少分高的不二选择✧(≖ ◡ ≖✿)
我果断加入LOOK大家庭,结(zhao)合(chao)实验代码,草率搓出了第一版
Main运行InputThread输入线程、Schedule调度器(我其实之前耦合在输入线程里面)、Elevator电梯线程
共享的数据类型只有Pesron记录人员请求信息、RequestTable管理人员请求信息
策略类Strategy,采用LOOK算法
梦幻开局,我还天真的认为:电梯月?就这??(๑>ڡ<)☆
@Override
public void run() {
while (true) {
if (mainRequestTable.isEmpty() && mainRequestTable.isEnd()) {
for (RequestTable requestTable : verticalRequestTables) {
requestTable.setEnd(true);
}
return;
}
Person person = mainRequestTable.getOneRequest();
if (person == null) {
continue;
}
int eleId = person.getBeginElevator();
verticalRequestTables.get(eleId - 1).addRequest(person);
}
}
在这次作业没什么存在性的东西,直接按照输入分配
对RequestTable里的所有方法统统上锁一劳永逸✧ (≖ ‿ ≖)✧
在Strategy类里遍历RequestTable里容器的时候别忘上锁,不然有几率触发Java.util.ConcurrentModificationException
public boolean reqSameDirection(int eleFloor, boolean eleDirection) {
//请求队列非空已判断
synchronized (request) {
for (Integer i : request.getReqMap().keySet()) {
if ((i > eleFloor && eleDirection) ||
(i < eleFloor && !eleDirection)) {
return true;
}
}
}
return false;
}


适逢清明假期,周一得知不能使用自由竞争的我缓缓裂开 º·(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )‧º·˚
抛开调度策略不谈(随机是我最后的港湾),重构启动!!!
本次作业新增RESET,将重构总结为四步
输入线程得到reset请求->mainRequest新增resetRequest请求队列数据结构
调度线程分配reset请求->eleRequest新增resetRequest数据结构
电梯类加入reset执行过程->放人->输出begin->返回请求->改电梯属性->输出end
策略类新增RESET建议
if (request.getResetRequest() != null) {
return Advice.RESET;
}
心比天高不自量力,遂开始研究影子电梯,苦于前人记载较模糊,只有“用计时代替sleep”,完全不会做(╯#-_-)╯╧═╧
不想写深克隆也不想考虑是不是线程安全(我至今觉得下面的代码有问题就是没被hack到hhh)
写了一个丑陋的shadow类,来负责计算
public Shadow(Elevator elevator) {
//一些有的没的。。。
this.time = 0;
//抽象开始了 我感觉线程不安全hh
ArrayList<Person> reqArr = new ArrayList<>();
reqArr.addAll(elevator.getEleRequest().getReqArr());
this.eleRequest = new RequestTable();
for (Person person : reqArr) {
eleRequest.addRequestClone(person);
}
this.eleMap = new HashMap<>();
ArrayList<Person> elePerson = new ArrayList<>();
elePerson.addAll(elevator.getElePersonArr());
for (Person person : elePerson) {
int toFloor = person.getToFloor();
if (eleMap.containsKey(toFloor)) {
eleMap.get(toFloor).add(person);
} else {
ArrayList<Person> persons = new ArrayList<>();
persons.add(person);
eleMap.put(person.getToFloor(), persons);
}
}
}
参考前人博客遍历6个电梯,找出能够使得新请求加入后整体运行时间结束最早的进行分配
丑陋抽象的实现了“影子电梯”,为第三次无法完全模拟电梯行为埋下祸根( >﹏<。)~
ResetRequest resetRequest = mainRequestTable.getOneReset();
if (resetRequest != null) {
resetElevator(resetRequest);
}
Person person = mainRequestTable.getOneRequest();
if (person != null) {
dispatch(person);
}
新增对应调度器内的resetElevator、dispatch方法分别上锁,确保不会同时执行
if (mainRequestTable.isPersonEmpty() && mainRequestTable.isResetEmpty()
&& mainRequestTable.isEnd()) {
if (isFinished()) {//每部电梯所有请求处理完毕 reqArr.isEmpty() && resetRequest == null
for (RequestTable requestTable : eleRequestTables) {
requestTable.setEnd(true);
}
return;
} else {//避免轮询
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
mainRequestTable所有请求处理完毕(reset & person)
输入结束
每部电梯所有请求处理完毕(reset & person)


本周新增DCElevator,对此我强烈建议成立影子电梯受害者联盟 º·(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )‧º·˚
秉持着菜就多练的原则,本周我灵活运用达芬奇睡眠法,倾情加入Buffer神教,继续深耕影子电梯
butttttttttt 缝缝补补到周六早八,还是有bug.........
一怒之下,转身向随机算法+随机地大小睡走去
if u wanna sleep ,u must sleep ——著名哲学家,在周六九点终于入睡的我
本来我通过罚时粗糙模拟了Gemini电梯的运行时间,但遇到了如下问题
//debug实录
//加锁了??你一被我深克隆的东西怎么还会被外界改??反思
//计算时间 改了一天还有问题??
//人怎么还要上锁啊?????
今天交了一波强测,发现全AC了,世界是一个巨大的笑话 ——4.19
于是转身向随机走去
public synchronized int randomDispatch(Person person) {
Random random = new Random();
int id = random.nextInt(6) + 1;
synchronized (电梯表) {
synchronized (A电梯表) {
synchronized (B电梯表) {
//......
if (isChange) {
person.setNeedChange(true);
}
if (isA) {
Output.printReceive(1,geminis.get(index).getId(),person.getId());
geminiAReqs.get(index).addRequest(person);
} else {
Output.printReceive(2,geminis.get(index).getId(),person.getId());
geminiBReqs.get(index).addRequest(person);
}
} else {//普通电梯
if (eleRequestTables.get(id - 1).getResetRequest() != null
|| eleRequestTables.get(id - 1).getDcRequest() != null) {
return -1;
} else {
Output.printReceive(0,id,person.getId());
eleRequestTables.get(id - 1).addRequest(person);
}
}
}
}
}
return 0;
}
仿照RESET指令,很容易复现DCRESET,区别在于DCRESET在1.2s执行结束后我选择终止电梯线程
原电梯线程在调度器里新建Gemini类(双子星!姐妹儿英语真有水平),管理新建AB两个电梯
继续沿用LOOK策略,因为我只要确保人员分配正确、运行特判就可以让它们在现有架构下正确工作,我们要做的是对Gemini电梯的移动特殊规范一下(电梯防撞)
最关键的电梯防撞:
我起初想不要让电梯占用TransferFloor,来了干完活就走,但人总是喜欢自己为难自己(๑>ڡ<)☆
探索之后,我写出了这样的shit
public void moveSpecial() {
if (Gemini电梯 准备来Transfer) {
while (floor.isOccupy()) {
floor.occupyOneAway(type);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (floor) {
floor.setOccupy();
try {
sleep((long) (1000 * moveTime));
} catch (InterruptedException e) {
e.printStackTrace();
}
eleFloor = stopFloor;
Output.printArrive(type, id, eleFloor);
Advice advice = strategy.getAdvice(eleFloor, elePersonNum,
eleDirection, eleMap, eleMaxNum,
type, stopFloor, elePersonArr);
if (advice == Advice.OPEN) {
openAndClose();
}
}
return;
} else if (Gemini电梯 准备离开transfer) {
//走
floor.awayTransfer();
return;
}else{
//正常行动
}
}
设置一个floor类来表示换乘层是否占用,内存AB两电梯的请求表
如果A想进入换乘层,floor被占用,就向请求表发送离开换乘层的请求,Asleep一下
另一部电梯(B)离开后, floor.awayTransfer()设置换乘层不被占用标志
此时A睡醒了进入换乘层进行放人操作
如果A主动离开换乘层(有人需要接), floor.awayTransfer()设置换乘层不被占用标志
一睡到底逃避锁的竞争,不愧是我T_T
直到今天复习OS的时候看到进程互斥的软件实现,我有种似曾相识的感觉……这错误集锦是在骂我!!!
和上次相比新增changeMan == 0
分配去换乘的乘客要坐上最后一班列车,我们才能放心和OO第二单元告别啊º·(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )‧º·˚ ~
public static synchronized void decreaceChangeMan() {
changeMan--;
}
public static synchronized void addChangeMan() {
changeMan++;
}
de出来的bug
可恶的Java.util.ConcurrentModificationException
线程不安全de遍历RequestTable里容器的时候一遍删除一遍遍历,别忘记上锁
第一次作业差点寄在这里了
public boolean reqSameDirection(int eleFloor, boolean eleDirection) {
//请求队列非空已判断
synchronized (request) {
for (Integer i : request.getReqMap().keySet()) {
if ((i > eleFloor && eleDirection) ||
(i < eleFloor && !eleDirection)) {
return true;
}
}
}
return false;
}
强测和互测
均没有hack和被hack 我们都好优秀(*>◡❛)
de出来的bug
强测和互测
强测多亏了伟大的影子电梯
互测进入了围城必阙副本
一时齐发众妙毕备!!!!因为设置RESET电梯不接客所以大量请求就会涌入唯一一部不在RESET的电梯T_T
[1.0]RESET-Elevator-6-3-0.6
[49.5]RESET-Elevator-1-3-0.6
[49.5]RESET-Elevator-2-3-0.6
[49.5]RESET-Elevator-3-3-0.6
[49.5]RESET-Elevator-4-3-0.6
[49.5]RESET-Elevator-5-3-0.6
[49.5]1-FROM-2-TO-11
[49.5]3-FROM-3-TO-11
[49.5]3-FROM-3-TO-10
。。。
这种数据我一刀连上自己能刀7个人º·(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )‧º·˚ (PS:我是文明人,这刀我就使一次)
此外还发现了一些同学reset调度上的一些bug
真是一次快乐刀人的体验 成功率高达33.3%╰ ( ´⌣` )╯
de出来的bug
AB电梯结束在停靠层,要在OVER之前先请它离开停靠层
if (request.isEnd()) {
if (type != 0 && eleFloor == stopFloor) {
return Advice.AWAY;
} else {
return Advice.OVER;
}
}
de不出来的bug
强测和互测
伟大的随机没有bug(๑ơ ₃ ơ)♥
房间里有最后一条RESET就爆炸的地雷
还有同学电梯防撞做的不到位(要我说就该随地大小睡)
还有同学调度器写的有问题,有请求处理不完就覆盖了,但抽不到这张bug卡T_T
起初对线程安全只有一个模糊的概念,不就是对RequestTable上锁?后期迭代老师建议将电梯与电梯线程分离,但因为偷懒我一意孤行将电梯保持共商共建共享开放包容。因此我遇到了数不清的线程安全问题T_T
关于读写锁,我也只是在理论课和实验课代码中浅尝辄止,因为偷懒我一意孤行只使用synchronized,性能差差的很安心T_T
我想如果这单元能重来一遍,我一定会好好重构一次线程安全的优雅的代码,可惜没如果º·(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )‧º·˚ ~
这单元的层次化设计非常清晰!基本架构沿用了三次作业
输入线程、调度线程、电梯线程三板斧
请求表、人员为共享资源
电梯运行与策略分离
良好的层次化设计让我每次新增代码量不多(思考量不少
生活不止代码,高分值得我加倍付出努力,但它绝不是我生活的主旋律 (^∇^☆)
美好的清明假期,无法割舍的是没有打成的明日方舟桌游,无法割舍的是凌晨的麦麦,无法割舍的是与同学海底捞小聚,无法割舍的是人大旁边的萨莉亚,无法割舍的是五道口的棋牌室,无法割舍的是朋友和稍纵即逝的青春(谁家6系人天天吃喝玩乐啊喂)
看到这里的大家都注意身体健康!
连续两周一坐坐一天对着密密麻麻数据debug,我能精准的记忆每位乘客不同调度策略的概率和我的bug出现概率
夜昏乍似灯将灭,朝暗长疑镜未磨……我该换新眼镜(眼睛)了(ฅ´ω`ฅ)
自我为难和放过自我之间的平衡
对于不打算深耕cs专业的我,着了什么魔非要写影子电梯呢??可能是被追求卓越的大家感染了吧hhhh
但我最后因为心得第一点和菜的原因,还是选择放过自我,随机真香啊suki!!!(ฅ´ω`ฅ)
每单元一遍:下次不会这么狼狈了!!!
下次真别来《失败的艺术4》了
结尾致谢
感谢亲亲舍友帮我找出来无数的bug
cozy!“你一句你别急真不晚,我就到了真江南”
Shae!267yyds!!存在即安心
luma!睡神!noSleep_noSleep
感谢耐心帮助、精神鼓励的良师益友们
感谢讨论区以及开发测评机的无私奉献的佬们
感谢OO课程组的倾情付出 给了我神奇的电梯月体验º·(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )‧º·˚ ~