第一次作業要求完成單部可捎帶電梯的模擬。
第一次作業中,我對於多執行緒程式設計理解還不深刻,只能先模仿實驗課練習的規範寫。
我採用了經典的生產者-消費者模型,由input執行緒產生請求,放入排程器物件requestqueue中,然後由elevator執行緒從中取出請求執行。
在排程器放入、取出請求以及結束置位的介面上,均以this為鎖進行同步,保證排程器是執行緒安全類。
在電梯中,我維護了乙個工作佇列儲存所有已經進入電梯的請求,使捎帶方面邏輯較為清晰。
排程演算法我採用了課程組提供的als,電梯空閒時獲取等待佇列中最先到達的請求為主請求,然後在電梯到達每一層時判斷這層是否有可捎帶請求,以及電梯內是否有請求到達,進行io互動。只有電梯把當前工作佇列中的所有請求全部送達後,才會重新尋找主請求。
當電梯中無人、輸入已經結束且等待佇列中無請求時,電梯執行緒結束。
**量:
方法複雜度:
在判斷可捎帶請求上邏輯較複雜。
中測:第一次提交ac。
強測:沒有出現bug,但效能分很差。後來分析了一下,對於單部電梯,採用scan/look演算法或sstf(貪心)演算法在效能上會好很多。
互測:沒有hack/被hack成功。
在第一次作業的基礎上,新增了動態初始化電梯數量(1~5部),引入了負數樓層和電梯最大載荷。
總體架構不變,初始化時由input執行緒根據輸入來建立電梯執行緒。
在獲取主請求的介面中,改用貪心演算法,將電梯當前的樓層作為狀態引數傳給排程器,排程器尋找乙個距離最近的請求作為主請求。
可捎帶請求的判斷邏輯修改為只會捎帶與電梯執行方向、主請求方向均同向的請求。獲取可捎帶請求時,將電梯當前可繼續裝載的最大人數、執行方向、主請求方向作為狀態引數傳給排程器,排程器尋找所有滿足條件的請求作為捎帶請求。
在多部電梯中,我使每部電梯的請求獲取盡可能的動態、隨機,簡化其捎帶任務,提公升單部電梯的利用率。
**量:
方法複雜度:
還是老問題,電梯io判斷專案較多較複雜。此外由於貪心尋找,排程器的獲取主請求介面也比較複雜。
中測:第一次提交ac。
強測:沒有出現bug,效能分較好。看來多部電梯使用貪心演算法效果是不錯的。
互測:沒有hack/被hack成功。
第三次作業在第二次的基礎上規定了三種電梯型別,每種型別的可達樓層、執行時間和最大載荷均不同,部分請求需要換乘,並且可以動態增加電梯。
在可達樓層、執行時間和最大載荷的不同方面,只需要在電梯中增加相應的字段,在工廠類中按需生成即可。具體判斷邏輯與第二次作業相同。
對於換乘請求,在排程器中新增乙個hashmap類物件wait作為等待佇列。當新請求到來時,判斷其是否為直達請求,如果是則直接放入請求佇列中,如果不是,則按照requestparser類的分析結果給出乙個換乘樓層(所有非直達請求都能分為兩段直達請求),構造兩個階段的personrequest,第一階段存入queue中,第二階段存入wait中。當有請求完成時,在wait中按id查詢其是否有第二段請求,如果有,則將第二段請求移至等待佇列中。
排程演算法完全不需要改動,延續第二次作業的貪心演算法。
電梯只有到達自身的可達樓層時,才會執行io函式判斷捎帶和進出。
對於單個電梯,當其工作隊列為空、等待佇列中沒有自身可以直達的請求,且輸入已經結束、wait中沒有第二階段請求時,電梯執行緒結束。
**量:
方法複雜度:
中測:第一次提交ac
強測:沒有出現bug,效能分較好。
互測:沒有hack成功,被hack成功了乙個rtle的點,但是觀察錯誤輸出發現中間莫名停頓數次,經過反覆的本地測試都無法復現且本地與評測結果相差甚遠,且原封不動再次提交即修復,搞不明白。
對於第三次作業,結合solid設計原則一一進行分析
srp(單一職責原則):
對於電梯類,設定了io、人員進出、電梯執行、更新方向等方法;對於排程器類,設定了放入請求、分割請求、獲取主請求、獲取捎帶請求等方法,分工較為明確,頂層邏輯清晰,沒有乙個方法負責兩件工作的情況,同乙個類中的方法也僅用於維護自身狀態。
ocp(開閉原則):
如果新增需求是有關電梯狀態的限制,我的設計是能夠在不改變現有**的基礎上新增功能的。但是如果是更為巨集觀的要求,比如電梯的檢修、增設樓梯、更複雜的請求實現,那就不可避免的要改動現有架構,說明在開閉原則方面還可以繼續完善。
lsp(黎克特制替換原則):
由於電梯類的方法目標較為清晰,在不同型別的電梯中含義均相同,只是更換了數值。且對於不同型別的電梯,我並沒有編寫多個類,而是在乙個類中增加字段,通過工廠模式構造不同的電梯,可以滿足lsp原則。
isp(介面隔離原則):
雖然本單元中我並未繼承除了runnable外的任何介面,但是電梯類中的方法到不同型別的電梯的對映過程可以看做是介面的實現。在這方面,由於單個方法的功能相對於整體都是不可或缺的,例如每個狀態的維護、人員io都有且僅有乙個函式來負責,其內部的子函式也都有各自獨立的功能進行復用,所以可以說介面是隔離開的。
dip(依賴倒置原則):
本單元作業中,最底層的模組是共享物件queue類以及一些採用單例模式的功能類如工廠類、樓層類和請求分析類等,而這些類可以被乙個或多個執行緒類共享,例如queue類和樓層類被輸入執行緒和電梯執行緒共享、工廠類被輸入執行緒例項化。但是排程器類中並沒有儲存每個電梯的狀態資訊,它無需了解電梯狀態,只需要電梯根據自己的狀態向排程器獲取特定請求即可,沒有迴圈依賴,在dip原則上做的較好。
本單元是我第一次接觸多執行緒程式設計,感覺同以前的程式設計,甚至同上一單元的程式設計相比都有著巨大的改變。每次任務不再需要邏輯超複雜的方法和花裡胡哨的類,但是編寫的思維難度卻只增不減,因為以人腦這個單執行緒去思考jvm的多執行緒是很有難度的。特別是在一開始,寫第一次作業的時候,對於執行緒之間、執行緒與共享物件之間的通訊,經常能把我想暈了。不過到了第二次、第三次作業的時候,由於掌握了多執行緒程式設計的技巧和邏輯,我很好的延續了以往的架構,很快就完成了任務,擺脫了第一單元重構三次的魔咒。多執行緒中,最容易出的錯就是死鎖、死迴圈、輪詢、停不下來等等。想解決這些問題,就要搞清楚wait、sleep、notify、notifyall如何使用、使用在哪、為什麼在這裡使用,多思考當多個執行緒都執行這條語句時會發生什麼,避免亂用,以上的問題都可以輕鬆避免。
在自動測試方面,我依然延續了第一單元的做法,那就是不寫(劃掉)。筆者在完成了課內的所有規定任務之後腦子裡只剩下玩,不得不說確實是一條懶狗。當然,為了在自動測試方面不落下,我學習使用了jprofiler這個監控軟體來確保執行緒安全,另外更大部分的自動測試是我找同學白嫖了好用的包和指令碼,一跑就靈(逃)。在下一單元中,我們會學習jml這種新的語言。到時候希望我能多多學習大佬們寫的評測指令碼,自己也寫一些簡單的評測機,做一點貢獻(上一單元就這麼說的)。
OO第二單元 多執行緒電梯
elevator request兩個執行緒。elevator執行緒主要負責乘客的接送和進出。request執行緒是接收乘客資訊。control是緩衝器,用來儲存elevator和request兩個執行緒共享的乘客佇列。以電梯當前樓層和執行狀態為基準,如果電梯是上行的,並且高於當前樓層還有乘客要進出就...
OO第二單元電梯作業總結
三次電梯作業,從實現單部多執行緒 相同的多部多執行緒到可以處理增加電梯請求的不同多部多執行緒電梯,是乙個對多執行緒程式設計從無到有的指南。多執行緒程式設計的優勢和用處不必多說,在oo os理論課上老師都已有詳盡的闡釋。整體而言,在個人學習的體會中,多執行緒程式設計的難點集中在 多執行緒概念的理解 包...
OO第二單元總結
本單元的作業總體來說比較愉快,畢竟不像上次一樣次次重構。本單元為電梯系列問題,涉及到多執行緒問題。簡單起見,我使用的是生產者 消費者模式。本次作業要求實現單部可稍帶電梯。看完題目後我認為生產者 消費者模式非常適合解決這個問題。本次電梯我採用的是look方法。本方法核心即在於電梯方向的判斷,這在dis...