OO第二單元電梯總結
目錄- OO第二單元電梯總結
- 架構模式
- hw5 一級生產者消費者模型with策略類分離
- 關于策略類的提取:
- 關于優化實作了:
- hw6 增加了兩個電梯子類的一級生產者消費者模型
- hw7 帶換乘的一級生產者消費者模型
- 執行緒的協作簡圖
- hw5 一級生產者消費者模型with策略類分離
- 同步塊和鎖
- 調度器設計
- bug與測驗 總結
- 強測&互測bug
- 遇到的記憶深刻的bug
- 本地測驗與測評機
- 心得體會
- 執行緒安全心得
- 層次化設計&設計原則
- 真情實感
- 架構模式
架構模式
Hw5, Hw6, Hw7三次作業架構基本沒有巨大變化,屬于增量的疊加開發
hw5 一級生產者消費者模型with策略類分離

第一次作業, 我做了兩種架構的嘗試, 寫了:
-
調度執行緒祭天型 單托盤

-
帶調度器執行緒的兩級托盤

在嘗試寫了兩種架構的基礎上, 我分析了一下兩種架構,
-
不對任何資料進行區分, 所有資料對電梯可見
-
是二級生產者消費者模型, 僅僅對每個樓進行劃分,每棟樓之間的請求隔離, 樓內請求電梯間是互通的
在我的兩種架構里, 第二種架構的scheduler執行緒僅僅負責取總托盤的請求,然后按樓區分發,本質上和你電梯直接去查找大托盤, 各取所需, 在功能上似乎沒有任何區別, 反而調度祭天,減少了一個執行緒, 首先會簡單不少, 其次, 每個電梯可見所有請求, 或許擴展性反而更好, 所以最終我選擇了1作為三次作業的基本架構,
總之,對于架構的選擇取舍就是:
要獲取高效簡潔解決問題的架構, 就得在調度自由度和懶癌(bushi, 簡單度之間做取舍
? ————沃茲基朔德

關于策略類的提取:
? 體現了DIP依賴倒置原則高級模塊不應該依賴低級模塊,而是依賴抽象介面,通過抽象介面使用對應的低級模塊
關于優化實作了:
-
量子電梯, 通過記錄上次arrive & close 時間, 減少了wait以后,再被喚醒,可以快速的度過本層(其實就是拿一部分沉睡時間當作運動中的時間)比如:
private void moveArrive() { try { Thread.sleep(max(400 + (lastArriveTime - System.currentTimeMillis()), 0)); // Thread.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } building = (building + dir + 5) % 5; // arrive MyOutput.println("ARRIVE-" + buildingNameList[building] + "-" + floor + "-" + elevatorId); } -
開關門400ms恒定保持
-
先下后上,盡量哆啦人(這不是共識嗎?但我和其他童鞋交流時發現他沒有,姑且算是吧
hw6 增加了兩個電梯子類的一級生產者消費者模型

這次作業中, 可以看到最令人神奇的就是:
- 加入了橫向電梯
- 動態新增電梯
- 不考慮換乘
這就意味著我們要為環形電梯設計新的的調度策略, 同時多個電梯這一次終于可以互相進行搶人, 這就帶來了競爭和分配如何選擇的問題
-
對于新增橫豎電梯,還存在行為上的差異, 所以我搞了兩個子類, 分別處理橫豎方向的電梯
-
動態新增電梯, 這里其實暗示我們搞執行緒池, 然鵝我也現在才發現, 如果以后有機會可以搞個執行緒池, 其次由于選擇了調度執行緒寄了天, 自然選擇
-
提前為換乘打余量,我將托盤中的
PersonRequest改成了ConcurrentLinkedQueue<PersonRequest>鏈表,具體怎么換乘hw7來說 -
環形電梯調度策略:
采用了類ALS基準策略, 效果還可以, 基本性能分都拿到了, 98+不戳的
Look也寫了,但是擔心出bug, 所以沒有采取這個策略物件, 用了ALS, 當然Look肯定寫好了是更快的
hw7 帶換乘的一級生產者消費者模型

可以看到基本沒改什么, 就是在Input類中加入了一個換乘拆分函式

執行緒的協作簡圖

主要就是
- 輸入執行緒,接收到輸出, 進行請求拆分, 或者創建電梯執行緒
- 電梯執行緒, 在while回圈中, 每次檢測是否wait或者return, 此后決定上下人,并依據上下人開關門, 最后決定方向并前進
同步塊和鎖
總結分析三次作業中同步塊的設定和鎖的選擇,并分析鎖與同步快中處理陳述句之間的關系
三次作業架構基本保持了一致, 且同步塊基本也保持了一致, 取最后一次作業的同步塊講解:
首先是 RequestTable.java中
// InputHandler 添加請求, Elevator送人到中轉站時
public synchronized void addRequest(ConcurrentLinkedQueue<PersonRequest> a) {
requests.add(a);
}
// personLeft維護的是 沒有送達目的地的人數
// InputHandler接收到請求時
public synchronized void addPersonLeft() {
this.notifyAll();
personLeft++;
}
// Elevator送人到目的地
public synchronized void subPersonLeft() {
personLeft--;
}
// 輸入結束
public synchronized boolean isInputEnd() {
return inputEnd;
}
// 輸入結束
public synchronized void setInputEnd(boolean inputEnd) {
this.notifyAll();
this.inputEnd = inputEnd;
}
// 上下是否沒有請求了
public synchronized boolean isUpDownEmpty(String buildingName) {
...
}
// 同理
public synchronized boolean isLeftRightEmpty(int floor) {
...
}
// 前方是否有請求
public synchronized boolean upDownRequestAhead(String buildingName, int floor, int velocity) {
...
}
// 人都送到了
public synchronized boolean noPersonLeft() {
return personLeft == 0;
}
Strategy類木有鎖, 不互斥的給出運動方向、上下人
public interface Strategy {
public Vector<PersonRequest> getPickUpRequests(Vector<ConcurrentLinkedQueue<PersonRequest> inside,
int floor, int dir, int capLeft);
public boolean turnAround(Vector<ConcurrentLinkedQueue<PersonRequest> inside, int floor, int dir, int capacity);
}
InputHandler中的鎖:
// 用于判停,判wait
private boolean checkWaitReturn() {
synchronized (requestTable) {
while (this.isempty() && requestTable.isLeftRightEmpty(floor)) {
if (requestTable.isInputEnd() &&
requestTable.isLeftRightEmpty(floor) && requestTable.noPersonLeft()
&& this.isempty()) {
requestTable.notifyAll();
return true;
}
try {
requestTable.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return false;
}
主要就是鎖住了RequestTable 防止 check & modify, read & write, write&write 的執行緒危險, 使得其互斥,
還有就是電梯在運行時需要查看一下情況,其實也可以放在RequestTable類里,
調度器設計
hw5的一個版本里, 我的調度器主要就是負責按樓層分發, 沒有任何特殊性,感覺名存實亡就是擺設, 所以就舍棄了調度器執行緒,
我的調度器執行緒沒了, 調度主要就是自由競爭,
同時換乘請求拆分時, 優化了一部分基準策略, 按概率隨機分配拆分樓層, 使得拆分樓層分布保持較為均勻
bug與測驗 總結
強測&互測bug
三次作業總共出現了一個bug, 就是我第一次作業最后提交的時候一著急, 不小心把上下人邏輯搞錯了, 導致可以承載的人數過多,wa了好幾個點,真的心痛, 血的教訓,
debug最重要的是按部就班的按debug流程來, 不要著急,不能東一榔頭西一棒槌,都可能錯過你的bug.
ps: 中測真的很弱, 感覺就是把你騙進來殺
? ————沃茲基朔德
比如在那以后我就規定了這么一套流程,防止我心急或者漏了什么,導致bug被miss過去, 看著圖一樂吧,畢竟我真是太粗了, 這些流程守好了, 別丟東西了

遇到的記憶深刻的bug

本地測驗與測評機
測評機三次作業都做了, 還和小伙伴一塊分享一塊測驗, 真不戳!評測機也是不斷迭代hhh
從最開始只能測一個人,到支持多個人一起pk速度
唯一不足就是對于超時應該及時掐斷, 我還沒來及整, python多執行緒有時間學一波吧
心得體會
執行緒安全心得
執行緒安全的心得在于,關鍵在于設計執行緒安全類, 通過執行緒安全類來保證各個執行緒的行為是安全的, 比如通過鎖的方法保證互斥, 通過資源也同時完成了執行緒間的協作通信, 關鍵在于選取合適的臨界區, 保證執行緒安全的情況下盡量小,
層次化設計&設計原則
三次作業基本是迭代的, 每次作業都在上次的作業上或是繼承, 或是新增了一些功能, 較好的實作了代碼復用
SRP: 職責應該單一,不要承擔過多的職責,體現在每個類只負責自己的行為, 比如:
? 電梯只負責開關門, 上下人, 停止, 轉向, 前進,
? RequestTable僅僅負責,根據電梯的資訊進行搜索并回傳資料,以及資料的更刪查改,
? InputHandler僅僅負責拿到請求、處理請求
OCP:修改軟體功能的時候,使得他不能修改我們原有代碼,只能新增代碼實作軟體功能修改的目的, 可以從三次代碼的結構是迭代的, 僅僅新增了部分代碼, 實作代碼復用,
ISP: 介面的內容一定要盡可能地小,能有多小就多小, 我將策略類的介面僅設定了2個方法,轉向、上下人,最小限度的保持了策略類對電梯的控制
LSP, DIP: 體現在策略類的分離體現了 ,前面說過了,就不重復了
真情實感
做首詩紀念一下
電梯月
? ——cywuuuu
歡送電梯月,
可惜有bug,
下次再努力,
爭取bugfree!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/464013.html
標籤:Java
上一篇:介面和抽象類有什么不同?
