2019年北航OO第二次博客總結

1、多線程電梯系列做業設計策略

1. 第一次做業——"FAFS傻瓜電梯"

第一次做業是先來先服務的"傻瓜電梯",我當時以爲這個設計未免太簡單了,因而就在傻瓜電梯的基礎上加上貪心算法,每次都執行電梯內外距離最近的請求(可是沒有行進中的捎帶)。因爲第一次沒有限制CPU時間,並且個人wait--notify用得不太熟,所以就採起了暴力輪詢的方式。我將我將調度器線程寫在了main函數中(這是個很很差的設計,第三次做業中將其改進)做爲一個線程,電梯做爲一個線程,輸入做爲一個線程一共三個線程。另外我設計了一個personlist的類以及一個person類,這個類裏面有一個屬性是flag,flag表示這我的的狀態,我規定了三種狀態(0:未上電梯1:在電梯內2:在電梯外),這樣我就不須要對personlist作remove操做,只須要每次都遍歷personlist看它裏面全部person對象的狀態便可,人上下電梯也就變成了對flag的更改。其中輸入線程阻塞式輸入,每次輸入以後轉換爲一個person對象加入到personlist中,而後電梯線程不斷的輪詢:判斷是否開門—>開關門—>尋找下一個最近的樓層—>去最近的樓層。在開關門階段電梯只負責開關門,人上下電梯由調度器負責,電梯開關門是將開關門標誌位置爲true,而後電梯sleep,這時調度器線程控制人的上下,當輸入線程結束後,調度器線程就會不斷判斷是否personlist中的全部人的flag均爲2,若是是並且電梯線程的門是否已關標誌位置true,則調度器線程結束,電梯線程我設置爲守護線程,在調度器線程和輸入線程都結束後它也會平穩結束。python

2. 第二次做業——"ALS可捎帶電梯"

第二次做業是同向請求可捎帶的「ALS電梯」。個人線程設置和上次相同,有兩點改變。第一,爲了限制CPU時間避免暴力輪詢,我使用了wait--notify讓線程在不需運行的時候wait,這是就須要多線程的協同與通訊。在輸入線程每輸入進來一次請求時,都會notifyAll一次,這時會喚醒電梯線程(我是採用標誌位置true+notifyAll來實現喚醒特定線程的),在輸入線程結束後會喚醒調度器線程看是否須要結束程序。在電梯線程中,每次開關門時要喚醒調度器線程讓它負責人員上下,每次關門要喚醒調度器看程序是否能夠結束。第二,在電梯運行過程當中每到一層都會判斷是否有同向可稍帶請求,若是有的話就執行捎帶,若是沒有的話繼續運行便可。算法

3. 第三次做業——"多電梯"

多電梯是一個很複雜的問題,個人思想就是化繁爲簡,ABC電梯被我設置成了三部ALS電梯,三個線程。每一個電梯有一個調度器,也是三個線程。這樣一共六個線程,電梯的三個線程爲守護線程。只要一個電梯可以執行的請求我就會讓一個電梯來執行,遇到了一個電梯執行不了的請求,我會設置一個固定的樓層讓它換乘,這些工做在電梯讀入請求前就作好了,這樣每一個電梯只管本身能夠接的請求,就至關於三個ALS的結合,感受也是我對"請求調度與請求實現分離"的理解。編程

2、基於度量對程序結構的分析

1. 第一次做業

1.1 複雜度分析

本文采用MetricsReloaded插件進行復雜度分析,其中OCavg表明類的方法的平均循環複雜度,WMC表明類的方法的總循環複雜度。設計模式

1.2 UML 類圖分析

本文采用IDEA內置diagram生成類圖。安全

優缺點分析:我新建了一個person類去存請求(就至關於人),personlist是請求的一個隊列,有三個線程。優勢是實現簡單,把電梯選請求都放到電梯線程中易於編碼;缺點是該分離的功能沒有分離實現,調度請求本應該是調度器的功能卻交給了電梯去實現。多線程

1.3 時序圖分析

1.4 SOLID法則分析

因爲沒有繼承和接口,所以對於LSP,ISP,DIP法則暫不考慮。架構

對於SRP,每一個類都有一個明確的職責。這個完成的不太好,個人電梯類既進行了運行電梯,又進行了選擇請求(至關於調度),應該讓每一個類的功能明確且單一。併發

對於OCP,感受實現的還能夠,以後的兩次做業均可以套用這個架構,電梯的運行機制基本不變,須要改變的只是調度的機制和擴充爲三部電梯,符合「無需修改已有實現,經過擴充來添加新功能」。函數

2. 第二次做業

2.1 複雜度分析

2.2 UML 類圖分析

優缺點分析:優勢是實現簡單,把電梯選請求都放到電梯線程中易於編碼,還有一點就是我把全部的輸出寫成了一個Output類,體現了面向對象封裝的思想;缺點是該分離的功能沒有分離實現,調度請求本應該是調度器的功能卻交給了電梯去實現。性能

2.3 時序圖分析

2.4 SOLID法則分析

因爲沒有繼承和接口,所以對於LSP,ISP,DIP法則暫不考慮。

對於SRP,每一個類都有一個明確的職責。這個完成的不太好,個人電梯類既進行了運行電梯,又進行了選擇請求(至關於調度),應該讓每一個類的功能明確且單一。

對於OCP,感受實現的還能夠,第三次做業就至關於三部這樣的電梯,電梯的運行機制基本不變,ALS調度也不須改變,符合「無需修改已有實現,經過擴充來添加新功能」。

3. 第三次做業

3.1 複雜度分析

3.2 UML 類圖分析

優缺點分析:優勢是實現簡單,把電梯選請求都放到電梯線程中易於編碼;把全部的輸出寫成了一個Output類,體現了面向對象封裝的思想;請求調度與請求執行分離,每一個調度器和電梯至關於一個第二次做業,三個這樣的系統就組成了此次做業,各種之間耦合度小,正確度高不宜出錯。缺點是該分離的功能沒有分離實現,調度請求本應該是調度器的功能卻交給了電梯去實現;三個電梯和三個調度器本能夠致謝兩個類而我卻在想保證正確性和性能想法的驅動下寫了6個類。

3.3 時序圖分析

3.4 SOLID法則分析

因爲沒有繼承和接口,所以對於LSP,ISP,DIP法則暫不考慮。

對於SRP,每一個類都有一個明確的職責。這個完成的還能夠,雖然電梯類既進行了運行電梯,又進行了選擇請求(至關於調度),可是每一個電梯和對應的調度器的這樣的小系統是功能很是單一的,系統之間耦合度低。

對於OCP,完成的很差,調度方法基本沒有什麼擴展能力,電梯越多複雜度越高,可擴展性低。

3、自動化測試與ReentrantLock

1. 自動化測試

因爲我這三次做業的hack次數和被hack次數均爲0,所以就來談談測試吧。多線程程序因爲它程序執行結果的不肯定性以及程序bug的難以復現性,再使用手動輸入測試的方法顯然不合時宜。這時咱們就須要利用一些自動化的測試方式來測試咱們的程序。主要的步驟是

1)使用python開啓一個子進程,啓動待測程序

2)經過sleep完成定時輸入,和第一步驟中開啓的子進程通訊

3)檢測輸出是否符合要求

2. Lock與ReentrantLock

這三次做業處於對正確性的追求,我一直在用synchronize加鎖,而在實現喚醒特定線程時我使用的方法是,設置特定線程標誌爲true而後notifyAll.然而,實際上徹底能夠用功能更爲強大的Lock和實現了Lock接口的可重入鎖ReentrantLock.Lock比synchronize的優點主要體如今三個方面:

1)可讓等待的鎖響應中斷

2)能夠知道是否成功得到鎖

3)能夠提升多個線程進行讀操做的效率

ReentrantLock+Condition能夠更加優美的實現喚醒特定的線程。

4、心得與體會

1. 線程安全

爲了提升程序的執行效率,咱們經常採起多個線程併發執行的方式。然而多線程中線程執行時序的不肯定性會致使線程不安全的事情存在。常見的read-then-write,check-then-modify模式的代碼都會形成線程的不安全。那麼在多線程編程中咱們如何能夠儘量地保證線程安全呢?有如下幾點值得注意的地方:

1)使用不可變對象,用final強制限制對屬性成員的修改

2)保證操做的原子性:使用Atomic***類型變量

3)保證更改的及時可見:violate關鍵字的使用

4)讀寫訪問的互斥控制:對須要同步的代碼塊加鎖

總結起來,要想保證你的設計是線程安全的,這裏有三個要素:

1)嚴格控制對象的發佈與共享

2)將共享對象設計爲線程安全類

3)線程類要保持簡介

2. 設計原則

這三次做業我用到了面向對象的一些設計模式,還有我本身的一些設計原則

1)若是全局只存在一個對象(好比調度器),則採用單例模式構造調度器對象

2)若是一個對象的狀態發生改變,須要另一些依賴它的對象收到通知並自動更新,則可使用觀察者模式

3)Worker Thread模式也是一種很好的設計模式,應用於咱們本次電梯做業可將請求調度與請求實現分離

4)實現簡單,架構清晰,正確度要比性能分更重要

3. 一些反思與展望

時光飛逝,轉眼間已通過去6次OO做業了,咱們本學期的OO征程也已通過半,這兩個月以來,雖然很是的辛苦,可是的的確確收穫了不少,雖然本身還有不少不足,可是對本學期OO的前半部分仍是比較滿意的,但願本身不斷努力,繼續加油!

相關文章
相關標籤/搜索