第一次做業——FAFS調度算法
1.設計策略設計模式
第一次做業是單部電梯的傻瓜調度,因此電梯的運做不是難點。對於初次接觸多線程的我來講,多線程之間的協同和同步控制是此次做業的難點。做業的總體思路我採用的是典型的生產者-消費者模式,請求輸入是一個生產者線程,調度器至關於一個倉庫,電梯則是消費者線程。安全
我採用的是共享對象來實現的多線程間的同步控制,輸入請求線程中有一個判斷輸入是否結束的共享變量,調度器中有一個當前隊列是否爲空的共享變量。關於多線程的協同,我採用的是輪詢的方法來控制電梯線程。第一次並不太會使用wait和notify等,致使採用暴力輪詢的方法CPU佔用率偏高。
多線程
2.基於度量分析程序結構框架
類圖函數
這四個類的功能較爲獨立且簡單。RequestInput 是輸入線程,Dispatcher 是調度器,ElevatorRun是電梯線程。輸入線程和電梯線程分別組合同一個調度器來實現線程間的交互。性能
分析結果中能夠看到ev, iv, v這幾欄,分別代指基本複雜度(Essential Complexity (ev(G))、模塊設計複雜度(耦合度)(Module Design Complexity (iv(G)))、Cyclomatic Complexity (v(G))圈複雜度(獨立路徑的條數)。OCavg和WMC兩個項目,分別表明類的方法的平均循環複雜度和總循環複雜度。學習
單一職責原則(SRP):符合,每個類和方法功能單一。測試
開放封閉原則(OCP):輸入請求類是符合這一原則的,但電梯類和調度器類是不符合該原則的。第一做業的調度算法比較簡單,對於以後優化的調度可擴展性不高。優化
里氏替換原則(LSP):因爲沒有使用繼承,未能體現
接口隔離原則(ISP):因爲沒有使用接口,未能體現
依賴倒置原則(DIP):不符合,電梯類和輸入請求類都依賴於調度器類,調用了其提供的一些方法,所以若是調度器類對外接口發生改動,則其餘兩類也須要改動。
3.BUG分析
在寫此次做業的過程當中,如何控制線程的結束真的難到了我。屢次嘗試,有徹底沒輸出的時候,有程序沒法結束的時候。最後在我debug半天以後(不過用的是輪詢方法)總算解決了這個問題。本次做業真的很簡單,實現以後測試是沒有bug的。強測第一次得滿分……
第二次做業——單部多線程可捎帶調度(ALS)電梯
1.設計策略
第二次做業在第一次做業的基礎上,大致框架是沒有改變的,依然是生產者-消費者模型。但此次調度算法是ALS算法,線程間的協同與同步控制我拋棄了以前輪詢的作法,採用了wait和notify等。
個人調度器內的隊列一直用的是LinkedBlockingQueue,它是個阻塞的線程安全的隊列,底層採用鏈表實現。
我採用的入隊方法是put。put方法 :若向隊尾添加元素的時候發現隊列已經滿了會發生阻塞一直等待空間,以加入元素
出隊方法採用的是take。take方法看起來就是put方法的逆向操做。若隊列爲空,發生阻塞,等待有元素;隊列不爲空,從隊首獲取並移除一個元素
線程間的協同與同步控制依然是共享對象的方法。不過當隊列爲空時再也不是輪詢方法,而是wait,每當put操做就喚醒沉睡的線程,若從隊列中讀到NULL(LookAtFirst函數在隊列爲空時,若輸入未結束則wait,不然獲得NULL)則電梯進程結束。
2.基於度量分析程序結構
類圖
相比於第一次做業,此次做業各個類的方法以及屬性較多。此次我添加一個Button類,其實仿照現實生活中的電梯按鈕。電梯線程有兩個Button類,一個外部按鈕,以樓層爲索引統計各個請求;一個內部電梯,以樓層爲索引統計電梯內的請求要到的目的地。關於該樓層的請求可否捎帶等方法實現都是在Button類內實現的。由於增長了Button類,Button的信息也須要同步,故而額外增長了不少方法來實現其同步。
這樣有好處也有弊端。有的人實現捎帶,是在每一個樓層遍歷調度器類的隊列,這樣只需維護一個隊列信息的同步,但這樣每次遍歷隊列所有請求,也是一種浪費。我增長Button類可使得操做變得更加直觀,實際上調度器類的隊列是按加入時間記錄的,而Button類是按樓層來記錄的。
ElevatorRun類較爲複雜,由於此次的調度算法再也不那麼簡單,增長了不少方法來實現捎帶。
依賴度分析度量了類之間的依賴程度。有以下幾種項目:
Cyclic:指和類直接或間接相互依賴的類的數量。這樣的相互依賴可能致使代碼難以理解和測試。
Dcy和Dcy*:計算了該類直接依賴的類的數量,帶*表示包括了間接依賴的類。
Dpt和Dpt*:計算了直接依賴該類的類的數量,帶*表示包括了間接依賴的類。
SOLID設計檢查
SRP:較符合,三個主要的類分別負責請求的獲取輸入,對請求的調度分配,電梯運行。
OCP:較符合。爲了可以實現多部電梯的ALS調度,我給每部電梯擴展了外部按鈕以及內部按鈕的功能,使得每一個電梯的運行變得獨立。這樣的框架使得我在第三次的做業只需改動調度器。
LSP:沒有使用繼承,未能體現
ISP:沒有使用接口,未能體現
DIP:不符合,由於個人類與類之間存在組合關係仍然出現了高層模塊依賴底層模塊的現象。
3 .BUG分析
在本身運行了部分數據後,個人程序並無出錯。固然在邏輯上我認爲也是沒錯的。不過多線程的BUG是手動測試測不出來的,對於沒有評測機的我來講不管是測本身的仍是別人的潛在Bug都比較困難。
4.電梯調度算法分析
其實此次做業個人性能分幾乎爲0。我採用的捎帶方式便是指導書中所述的最原始的主請求捎帶。個人調度在去接主請求的路上是不能捎帶的。哎……都怪本身懶,性能分奇低。
我認爲比較優化且符合實際狀況的,即既能夠儘可能縮短總運行時間又能夠照顧每一個請求的用戶體驗的算法是SCAN算法。電梯除了靜止的狀態,老是上行以後下行,下行以後上行,即在一趟來回中解決儘量多的請求。按照樓層順序依次服務請求,讓電梯在最底層和最頂層之間連續往返運行,在運行過程當中響應處在於電梯運行方向相同的各樓層上的請求。
由此看來,我以前的捎帶算法是靠主請求來決定電梯運行的方向的,而scan算法是靠運行過程當中請求分佈樓層狀況來決定運行方向的。其實在我實現了Button類以後,scan算法是很好實現的。啊啊啊……,後悔當初沒有勤快一點!!
第三次做業——多電梯
1.設計策略
第三次做業要求模擬多部智能電梯,而且存在轉乘的狀況。單獨電梯我仍是採用的ALS捎帶調度算法。此次改動較大的是調度器類。
此次採用了兩層調度的策略。對於須要轉乘的請求,我在調度器內將其拆分爲兩個請求。而後讓第一個請求分配進優先級較高的電梯。當某個請求從電梯內出來,我會判斷該ID是否須要轉乘,若須要轉乘,則對其第二個請求分配入當前優先級最高的電梯。一個ID的拆分指令之間的協同,我是經過調度器類的一個Button類來實現的。其實際爲一個以ID爲Key的hashMap,只有當前ID的第一個請求完成時,才能將第二個請求分配入相應的電梯隊列(此次調度器類有三個電梯的隊列。)
電梯運行結束的控制條件爲輸入結束且調度器類中的hashMap爲空。這樣我就能保證完成所有任務,不會提早結束了。
2.基於度量分析的程序結構
類圖
此次做業在第二做業的基礎上只是調度器類作了較大改動,大致框架未變。
由上圖可見,指令拆分的中間樓層的計算較爲複雜,以及對於當前請求優先電梯的計算也較爲複雜。其實只是判斷條件比較多而已,並未以爲多複雜啊……
由上圖可見,個人類之間的依賴關係是比較多的。我採用的設計模式是觀察者模式,訂閱者(多部電梯)會依賴觀察者,觀察者須要通知訂閱者因此也要依賴它們。對於某些大佬的上圖中Cyclic一列爲0,不知道是怎麼辦到的。既然要實現多各種之間的信息交互,是怎麼減小類之間的依賴關係的呢?望賜教!
SOLID 分析
SRP:邏輯上認爲這幾個類的功能是比較單一的。不過由上面度量結果圖能夠看出,調度器的代碼規模大且控制分支多。
OCP:符合,若是再增長電梯的數目,只需對調度器類增長几個電梯的隊列便可,相應方法的改動也不多。
LSP:未使用繼承,沒法體現
ISP:未使用接口,沒法體現
DIP:不符合。
3.BUG分析
感受此次線程單元的BUG仍是主要出如今如何控制線程結束以及保證線程安全上。
強測中我製造了一個40條指令的輸入,只hack到了一我的。能力有限,我也想要強大的評測機……
心得體會
其實我以爲我在多線程安全方面仍是能夠優化的。爲了保險起見,我給不少方法都加了鎖,其實這其中必定是有多餘的狀況。多個線程或方法之間的同步與互斥是須要本身考慮來化簡的。
我感受一個單元的時間對於咱們徹底掌握多線程的知識點是徹底不夠的。其實只要你膽子大,不多的線程知識是徹底能夠搞定這些做業的。就算你使用了多線程的知識,這三次的做業咱們也只是對個別知識點使用的比較多。由於瞭解的不夠多,知識掌握的不夠全面,咱們的實現方法變得很單一。一直有種只知其一;不知其二的感受。固然"師傅領進門,修行看我的」,這些知識體系的完備仍是須要咱們花大量的額外時間來學習的。
加油啦,本身千萬不要淺嘗輒止啊,不要作一隻井底之蛙!共勉!