經過這三次做業的訓練,初步掌握了多線程的設計方法。這三次做業讓我對這門課有了更深入的認識,這門課的確是一門重課,不只是大難度、高強度,還有它的持久性,吳際老師稱其爲「崑崙課程」絕不爲過。這是身體與心理的雙重礪煉。安全
本次做業模擬出租車的乘客呼叫與應答系統,訓練線程安全設計方法,同時應用面向對象分析方法和設計原則來開展分析和設計。多線程
做業涉及的對象主要是地圖、出租車和出租車調度系統。乘客的請求從控制檯輸入,乘客發出請求後,系統向以乘客爲中心的4x4區域內的出租車廣播,收到廣播的出租車進行搶單,廣播有必定的時間窗口,窗口關閉後,系統從全部搶單的出租車中,根據出租車信用和出租車與乘客的距離來選擇一輛合適的出租車。出租車服務結束後,須要把這次服務的路徑、時間等信息輸出到文件中。性能
下面這張圖片是此次做業的類圖。主要的類有出租車Taxi類,調度Scheduler類和地圖RoadMap類。從線程的角度看,共有100個出租車線程,因請求而產生的調度線程,主調度線程,輸入處理線程和主線程。輸入處理線程負責接收控制檯輸入並處理,主調度線程對每一請求建立一個調度線程,調度線程在4x4區域廣播而後選擇一輛出租車,出租車服務結束後,調度線程輸出信息,而後結束。線程之間的關係能夠從後面的協做圖中看到。測試
RoadMap能夠存儲地圖,找出兩點間的最短路徑,判斷由某點向某個方向是否有路。路徑是一個Path對象,由方向+距離表示。做業中每條路的長度都相同,因此就省略了距離信息,只記錄方向。Path對象是不可變的,所以是線程安全的。整個程序裏只有一個RoadMap對象,但把它設計爲不可變對象,所以也是線程安全的,不一樣的出租車能夠同時調用shortestPath()方法。優化
因爲要輸出出租車的軌跡,出租車某一時刻的狀態,爲了管理這些信息,分別用PathRecord對象和TaxiInfo對象來記錄。PathRecord對象是可變的,能夠添加新的軌跡。TaxiInfo對象是不可變的。此外,不可變對象還有Request對象,Point對象,這些不可變對象都是線程安全的。編碼
出租車類有serve(), wander(), rest(), takeOrder()方法,分別對應出租車的服務,等待,中止,接單狀態。出租車線程主要與調度線程進行交互,所以,將線程協同與同步控制的重點放在出租車線程和調度線程的設計上。線程
圈複雜度最大值是13,主要來自於RoadMap的構造方法和出租車類的run()方法。NestedBlockDepth最大值是4,主要是兩層循環加上if-else嵌套致使的。總共有三處三層嵌套的代碼和三處兩層嵌套的代碼。設計
代碼總行數約是860行,其中Taxi類,Scheduler類和RoadMap類各佔約200行。方法代碼總行數約570行,每一個方法代碼行數最大值是30,平均值是6.2,方法總數是92個。指針
此次做業的bug是忽略了對出發地和目的地相同的請求的處理,以及出租車接單時正好在乘客的位置上的狀況。這兩種狀況都會致使在尋找最短路徑構造Path對象時出現空指針。我認爲致使問題出現的緣由有以下:rest
我認爲此次寫的做業優勢以下:
缺點以下:
每一個方法代碼量少,但方法數目多,這一點我不知道算優勢仍是缺點。從上面截圖中能夠看到方法的平均代碼行數是6.2,但方法數卻有92個。對於閱讀代碼者來講,過多的方法數致使難以掌控總體結構,單個方法太龐大致使難以理解局部。若是您願意同我分享您的看法,請在下面評論。
做業內容是實現一個監控程序,針對給定監控範圍內的監控對象,以掃描方式探查監控對象相關屬性的變化,從而觸發規定的處理動做。監控範圍指計算機文件系統中的一棵目錄樹,監控對象則是位於監控範圍內的具體文件。處理動做包括恢復,記錄詳細信息,記錄概要。做業的目標是訓練針對線程安全問題,如何平衡線程訪問控制和共享對象之間的矛盾。
從線程的角度上說,一條監控命令對應一個監控線程,和一個文件掃描線程。Monitor類能判斷監控對象的觸發器(包括重命名,移動,大小改變,最後修改時間改變)是否觸發,並執行相應處理動做。Snapshot類是文件屬性的快照,它的結構就是一顆目錄樹。SnapshotManager是快照管理類,FileScanner是文件掃描線程,它建立快照並把快照交給SnapshotManager管理。Analyzer類和TaskPerformer類分別是快照分析類和任務執行類,監控線程(Monitor)從SnapshotManager中取出最近的快照,並交給Analyzer分析,若是觸發,則讓TaskPerformer執行處理動做。Summary和Detail類是管理詳細信息和概要的類,這些信息會被定時寫到文件中去。
Snapshot對象是不可變的,它記錄有掃描時刻目錄下全部文件的屬性,是一個遞歸的結構,所以在分析快照時使用起來比較方便。TaskTuple對象也是不可變的,記錄有一條監控命令對應的監控對象,觸發器及任務。Summary類、Detail類、SnapshotManager類都被設計爲線程安全的類,由於全部的監控線程都共享同一個Summary和Detail對象,可能同時被多個線程訪問,SnapshotManager在文件掃描線程和監控線程間共享,也須要是線程安全的。SafeFile是線程安全的文件訪問類。
圈複雜度最大值是12,來自對輸入處理的方法。經過對過去幾回做業的度量分析,我發現輸入處理的圈複雜度較高,多是由於須要對多種輸入錯誤的狀況作處理,若是您有什麼下降複雜度的方法,歡迎在下方評論。
嵌套深度最大值是5,共有1個4層嵌套和3個3層嵌套的代碼(方法內部)。嵌套深度最大值來自Monitor的run()方法,循環和try-catch就佔了兩層,再加上if-else分支又佔了兩層,總共就是4層。其餘幾個3層嵌套都上是一層循環加上兩層if-else。這種多層嵌套的代碼可讀性不強,正確性也難以判斷,因此要儘可能避免。
全部屬性的個數是50,方法數是69,總代碼量約670行。每一個方法代碼行數,平均值是6.4行,大部分在10-20行,有兩個方法是30來行。
此次做業的bug是,監控重命名時,若同一目錄下有多個大小相同,最後修改時間相同的文件,就會判斷出錯。判斷重命名的標準是原來的文件消失(絕對路徑找不到了),新增了一個文件,這個文件的大小和最後修改時間與消失的文件相同。我忽略了「新增」的要求,就出現了這個bug。
優勢以下:
缺點以下:
本次做業是設計一套由3部電梯組成的多電梯調度系統,經過採用線程機制,在第三次做業所實現程序的基礎上完成新的調度系統程序。電梯可以支持捎帶,調度系統按照運動量均衡策略來調度樓層請求。
因爲此次寫的程序類太多,類的關係錯綜複雜,就不給出類圖了。下面這張圖是一個大致的結構,每一個方框表示一個對象,每一個橢圓表示一個放請求和取請求的托盤(由於第一次寫多線程程序不太熟悉,因此就顯式地用「托盤」來表示)。單個箭頭表示請求的流動方向,兩段都有箭頭的,是表示兩個線程間有交互。RequestSimulator是請求模擬器,它從控制檯中讀取輸入,產生請求。MultiScheduler是總的調度器,它負責將電梯直接請求分發給各部電梯,而且對樓層請求以捎帶和運動量均衡策略進行調度。再往下走是Scheduler,它負責對單步電梯的調度,例如處理捎帶請求等等,這就歸約到了第三次做業的問題。
圈複雜度最大值是20,來自Scheduler的調度方法。代碼總行數約1200行,方法總數是151個。能夠發現後面兩次做業(6-7)沒有達到這麼大的規模,緣由多是電梯調度邏輯的複雜性。
這次做業bug主要有兩個。一是對輸入的處理上,一行多個空請求的處理與說明文檔描述不符,是我疏於測試的問題,若是測試了就可以發現。二是樓層請求的捎帶有問題,具體是什麼問題就沒有深究了,我猜想多是邏輯上的問題加上對線程機制不熟悉的問題。
缺點以下:
因爲距第5次做業的時間比較久遠了,代碼寫得混亂,不忍直視,因此就簡略地分析一下。
感謝您可以花時間閱讀本文。這是我對本身三次做業的總結,大部份內容都是對個人做業的分析,可能讀者不太瞭解具體細節,因此,寫做時我儘量避開細節,這樣就顯得有些寬泛。若是您有疑問或者建議,請在下方評論。