本單元共三次電梯系統設計做業,難度遞增,架構能夠層層嵌套(每次都設計好的話),趣味頗豐。電梯的款式(奇怪的描述)均爲目的選層電梯,即乘客可在電梯外發送本身想要去的樓層。java
乘客的請求包括出發樓層和目的樓層與id,每位乘客都有本身獨有的id,一個id只能對應一個請求,本單元不需考慮WF檢測。算法
第一次做業電梯名爲單部多線程傻瓜調度(FAFS)電梯。傻瓜調度就明確指出不須要關注電梯性能,只須要完成接送人的任務,所以我菜用了較爲直觀的設計思路:安全
電梯屬性包括:1個請求隊列(屬於本電梯還在電梯外某樓層等着的人)、1個電梯內部隊列(電梯內部的人)、電梯如今的樓層。性能優化
類設計包括:Main(輪詢接受輸入)、Elevator(執行單元)、Waiter(請求類)、Inner(電梯內請求類)。共4個類多線程
運行邏輯以下:架構
一、Main函數輪詢(輸入接口等待輸入,無輸入時阻塞不佔用CPU)接受輸入的請求,將請求發送給電梯。函數
二、若輸入NULL,則由Main函數向電梯發送中止信號。性能
三、電梯輪詢依次判斷如下內容:測試
(1)、若電梯內部隊列不爲空,則執行電梯內乘客請求(將乘客送至指定樓層)優化
(2)、若請求隊列內如有請求,則執行該請求(電梯先去接乘客,而後將乘客送至指定樓層)
(3)、輪詢結束條件:請求隊列爲空 && 電梯內部隊列爲空 && 接收到中止信號
總結:本次做業未使用調度器(確實一開始沒想到有這種高級策略),但由於本次做業較爲簡單,並未受到影響。瞭解到了ArrayList線程不安全,以及簡單的多線程運行邏輯,爲第二次做業鋪設了基礎。
本次做業電梯名爲單部多線程可捎帶調度(ALS)電梯。可捎帶是本次做業核心,如何完成「捎帶」成爲本次做業的難點。設計架構沿用第一次傻瓜電梯的架構(可貴的沒有重構),設計思路以下:
電梯屬性包括:1個請求隊列(屬於本電梯還在電梯外某樓層等着的人)、1個電梯內部隊列(電梯內部的人)、電梯如今的樓層。(徹底沿用第一次的電梯架構)
類設計包括:Main(輪詢接受輸入)、Elevator(執行單元)、Waiter(請求類)、Inner(電梯內請求類)。共4個類(徹底沿用第一次的電梯架構)
電梯執行邏輯:電梯只執行主請求(電梯內最先進來的那我的),即電梯內部隊列裏的第一個元素。
接人邏輯(捎帶判斷):接人的條件需知足如下條件:(1)、捎帶請求與電梯主請求同方向。(2)、捎帶請求的出發樓層位於電梯主請求出發樓層與目的樓層之間(電梯到達某一樓層後,經過對請求隊列的循環判斷來實現,若知足條件,則執行捎帶)。
運行邏輯以下:
(電梯每到一層,對請求隊列循環判斷)
一、Main函數輪詢(輸入接口等待輸入,無輸入時阻塞不佔用CPU)接受輸入的請求,將請求發送給電梯(將接受到的請求加入電梯的請求隊列),並喚醒電梯(執行notify)。
二、若輸入NULL,則由Main函數向電梯發送中止信號。
三、電梯非輪詢依次判斷如下內容:
(1)、若電梯內部隊列爲空 && 請求隊列爲空 && 未接收到中止信號 :電梯執行wait()操做(電梯線程由執行態轉換至waiting態,等待被notify)
(2)、若電梯內部隊列不爲空,則執行電梯內乘客請求(將乘客送至指定樓層),同時進行捎帶判斷。
(3)、若請求隊列不爲空,則執行該請求(電梯先去接乘客,而後將乘客送至指定樓層),同時執行捎帶判斷。
(4)、電梯中止運行條件:請求隊列爲空 && 電梯內部隊列爲空 && 接收到中止信號
總結:本次電梯主要難點在於實現捎帶邏輯,個人捎帶邏輯也許不是最優化的(從系統總運行時間來講),但能夠知足題目的要求。本次實驗瞭解了wait()和notify()的使用方法,初步接觸了 synchronized關鍵字的使用,利用先有請求後執行這一邏輯避開了「電梯wait但無人喚醒」的狀況,本次做業作完以後才瞭解到「調度器」這一律念,雖然沒在此次做業中體現,可是提早完成了調度器的設計,爲第三次終極智能電梯打好了基礎。
此次實驗因爲個人疏忽大意(arrive輸出時間疏忽),強側分數有點噁心,可是整體上玩的仍是很開心。
本次做業電梯名爲多部多線程智能(SS)調度電梯。「多部」、「智能(電梯已是成熟電梯了,會本身分辨請求了)」是本次做業核心。電梯的設計架構沿用第二次捎帶電梯的架構(可貴的再次沒有重構),同時實現了調度器線程,調度器與電梯存在上下級的關係,設計架構以下:
本次電梯與前兩次電梯具備電梯屬性的不一樣,包括如下內容:電梯容量,電梯可達樓層,不一樣電梯速度不一樣(和調度策略的設計有關)。
電梯屬性包括:1個請求隊列(屬於本電梯還在電梯外某樓層等着的人)、1個電梯內部隊列(電梯內部的人)、電梯如今的樓層、電梯名、電梯容量、1個電梯可達樓層的ArrayList(方便使用contains方法)。(部分沿用第二次的電梯架構)
類設計包括:Main(輪詢接受輸入)、Elevator(執行單元)、Waiter(請求類)、Inner(電梯內請求類)、Dispatch(調度器)。共5個類(部分沿用第二次的電梯架構)
電梯執行邏輯:電梯只執行主請求(電梯內最先進來的那我的),即電梯內部隊列裏的第一個元素。
接人邏輯(捎帶判斷):接人的條件需知足如下條件:(1)、捎帶請求與電梯主請求同方向。(2)、捎帶請求的出發樓層位於電梯主請求出發樓層與目的樓層之間(電梯到達某一樓層後,經過對請求隊列的循環判斷來實現,若知足條件,則執行捎帶)。
調度原則:Main函數發送請求給調度器線程,調度器線程將請求存入調度器內請求隊列,將請求根據須要分紅4個請求:一、乘客可乘A電梯直達目標樓層。二、乘客可乘B電梯直達目標樓層。三、乘客可乘C電梯直達目標樓層。四、乘客須要換乘,執行換乘邏輯。
調度器換乘邏輯:根據三部電梯的可達樓層分析,乘客在起始樓層和目標樓層之間選擇一部換乘電梯並選擇一個換乘樓層(以先A後B後C原則,由於A電梯最快,這個的算法並很差),而後將乘客標記爲須要換乘,將請求發送給電梯。
電梯換乘邏輯:若電梯發現將要出電梯的某乘客爲換乘乘客,則向調度器發送一個新請求,將換乘乘客信息從新發送給調度器(即將換乘樓層變動爲起始樓層,目標樓層不變,乘客id不變),新請求將喚醒調度器,並執行調度器內換乘乘客計數器--(加鎖)。
調度器使用單例模式,即私有構造並設立getinstance()方法。
一、Main函數輪詢(輸入接口等待輸入,無輸入時阻塞不佔用CPU)接受輸入的請求,將請求發送給調度器(將接受到的請求加入電梯的請求隊列),並喚醒調度器(執行notify)。
二、若輸入NULL,則由Main函數向調度器發送中止信號,並結束Main函數。
一、若調度器請求隊列爲空 && (未收到Main函數發出的中止信號 || 存在換乘乘客) ,則執行wait()操做,等待被喚醒。
二、從調度器請求隊列中取出一個乘客,將該乘客根據調度原則,將請求分配給三個電梯中的一個(每一個電梯都有本身內部的請求隊列,即該電梯可以接的人,電梯對於調度器請求隊列一無所知),並從調度器請求隊列中移除該請求。若發現乘客須要換乘(執行調度原則4),則將調度器內換乘乘客計數器++(加鎖)。
三、若調度器請求隊列爲空 && 收到Main函數發出的中止信號 && 不存在換乘乘客 ,則向三部電梯發送中止信號,並中止調度器線程
電梯非輪詢依次判斷如下內容:
(1)、若電梯內部隊列爲空 && 請求隊列爲空 && 未接收到中止信號 :電梯執行wait()操做(電梯線程由執行態轉換至waiting態,等待被notify)
(2)、若電梯內部隊列不爲空,則執行電梯內乘客請求(將乘客送至指定樓層),進行捎帶判斷,進行電梯換乘邏輯判斷。
(3)、若請求隊列不爲空,則執行該請求(電梯先去接乘客,而後將乘客送至指定樓層),進行捎帶判斷,進行電梯換乘邏輯判斷。
(4)、電梯中止運行條件:請求隊列爲空 && 電梯內部隊列爲空 && 接收到調度器發出的中止信號
設計思路:此次換乘電梯最終構想是將換乘乘客標記,等到須要換乘時,由電梯向調度器輸送請求,可是這麼設計是不符合實際的,應該由調度器統籌安排電梯,電梯做爲執行單元,不該該影響調度器的工做,但由於我的能力有限,確實沒能想到一個好辦法,實現和電梯低耦合關係的調度器。
總結:本次電梯主要難點在於實現換乘,個人調度邏輯有大優化空間,可是由於我的能力有限,就沒有追求性能優化(怕改很差改出一堆bug)。個人換乘方法決定了個人調度器的中止條件,這個中止條件是經歷了不少次輸出debug才最終肯定的,確實對於多線程的理解有了很大幫助。很感謝助教團隊的辛勤構思,要否則我是沒有這個自信,僅僅第二次做業,就完成「不重構代碼」這一重大進步。此次的方法複雜度教前兩次有了一個明顯提高,但我設計調度器以前,就儘量下降調度器和電梯的耦合性,可是最後沒能想出一個較好的辦法,他們之間仍是存在雙向的信息傳遞。
第一次第二次做業bug主要在於,輸出方面的小瑕疵,尤爲是第二次做業,在輸出到達樓層和開關門之間,很容易有邏輯上的疏忽。
第三次做業bug主要在於換乘,換乘的是否合理,是否存在「電梯殺人狀況」,是否存在「電梯過早中止的狀況」,這是我遇到的bug中較爲典型的
多線程bug單步調試已經很難進行,因此採起了輸出調試的辦法,這種辦法也是比較高效的,較好的解決個各類bug。
多線程程序會出現,bug存在可是很難復現的狀況,因此找到一組比較好的數據,進行屢次提交,能夠較好的找到一些「強測倖存者」。
電梯單元讓我清晰瞭解了java多線程的精妙,很明顯的感受到本身在一次一次做業中,邏輯不斷完善,設計思路不斷清晰。線程安全類的使用,物理鎖與邏輯鎖的使用,線程喚醒的細節,都是在一次又一次試錯中慢慢摸索到的。用一個數據屢次測試本身的程序,有時會出現bug,可是再復現起來可能要通過不少次的調試,這個過程是艱難的,但發現bug併成功解決收穫的快樂和知足,也是很難用言語形容的。只有每次做業都盡心盡力,才能感受到本身在不斷進步,之後的oo做業也會更努力的。