OO第二單元總結 多執行緒電梯

2022-11-24 18:51:13 字數 2801 閱讀 6660

任務:單部多執行緒傻瓜排程(fafs)電梯

實現:3個執行緒,單例模式

thread1:主執行緒

thread2:輸入處理執行緒

thread3:電梯執行模擬執行緒

構建一個共享物件,用於put、get請求,該物件全域性唯一,被thread2和thread3共同擁有,給該物件的方法加同步鎖synchronized,以確保請求佇列的安全。

通過wait、notify,使電梯和輸入處理執行緒交替進入共享物件的臨界區執行。

電梯模擬行為:從佇列取一個請求,獲取其fromfloor、tofloor、personid,一次只進入一個請求、將該請求完成後再進入臨界區獲取新的請求,當取得請求為null時,表示沒有請求了,結束電梯執行緒。

任務:單部多執行緒可捎帶排程(als)電梯

實現:總體架構與第一次沒有太大變化,只是改了排程器的排程方法和電梯相應的請求處理行為

排程器主要method:

mainget():返回請求佇列第一個元素,傳給電梯,作為其主請求;

passget():傳入電梯的當前樓層和其主請求,返回可捎帶請求佇列;

nobody_passget():傳入電梯當前樓層和其主請求的fromfloor,返回電梯接主請求的途中可捎帶請求(相當於一個小優化);

電梯requesthandler():電梯為空時,與排程器互動獲得主請求,然後從當前樓層出發前往主請求的fromfloor,途中在每一層呼叫nobody_passget(),在主請求進入電梯後,執行主請求,在每一層呼叫passget(),並且在每一層進行判斷,調整主請求為電梯乘客佇列中的passger.get(0)。

任務:多部多執行緒智慧(ss)排程電梯

實現:電梯架構依然沒有太大變化,仍然是單例模式。

對於每個電梯,採取als策略。

給personrequest新增一個bool變數canin,true代表活躍(可進入電梯),flase代表休眠(暫時不可進入電梯),其作用為:由於特殊請求會換乘,因此排程器會將這類請求進行拆分成兩條請求,前請求進入電梯,並將後請求的canin設為false並留在請求佇列,在前請求到達換乘樓層並出電梯後,啟用後請求(即將後請求的canin設為true)。

uml

complexity metrics

uml

complexity metrics

umlcomplexity metrics

度量分析:

前兩次作業的複雜度都較低,第三次作業中排程器類的總迴圈複雜度和平均迴圈複雜度都較高,用於拆分請求的方法複雜度也很高,主要原因是該method中用了大量的多重巢狀的條件控制語句和迴圈。

設計原則分析:

由於沒有使用介面,只繼承了thread類,此處不談lsp和isp;

基本上三次作業基本符合srp原則,每個類或方法都有一個大致明確的職責,但其實還能進一步明確各方法的職責;

在第一次電梯的基礎上,只需擴充套件排程器的排程方法,即可實現als排程,可見符合ocp原則;

在第三次作業中,電梯類和排程器類的互動是依賴於抽象類實現的,並沒有依賴於電梯類的具體物件進行互動,因而滿足dip原則。

前兩次作業沒有bug,但是第三次作業在強測中幾乎爆零,這個bug是由於思維定式,每部電梯在每層樓都和排程器進行了互動,因而導致電梯在不能停靠的樓層進行了開關門動作,增加一條判斷語句即可修復該bug。

很顯然,本單元由於涉及到了多執行緒,又由於輸入的定時投放問題,傳統的hack策略已經不奏效了,所以我使用評測指令碼來實現定時投放,並對其結果進行自動化測試。

第一單元的作業,測試資料很容易構造,輸出只有一個表示式,對於不太長的資料,基本可以通過肉眼判斷其正確性,但電梯作業的輸出是十分長的,尤其是第三次作業各電梯的輸出相互交叉,難度更大。另外,電梯作業的bug可能很難復現,有時候評測機也會誤判。

本單元設計到了多執行緒,而多執行緒很重要的一點就是其執行緒安全,也是多執行緒的難點之一,通過對共享物件的方法加同步鎖來實現臨界區互斥,利用wait()、notifyall()來調節執行緒進進入臨界區的順序,當然還有更多的互斥機制,可以彌補synchronized的一些缺點,例如高併發下會損失效率。這三次作業我都採用了單例模式,這使得我的三次作業架構基本相同。在設計層面上,儘量符合solid原則,可增強**的拓展性、複用性。這三次作業,不僅使我接觸了多執行緒,也讓我對oo設計層面有了更深的理解。

最後就是,針對我第三次電梯作業大翻車的情況,告誡自己,千萬不要面向資料程式設計!!!