OO第二單元總結

第五次做業

本次做業,須要完成的任務爲單部多線程傻瓜調度(FAFS)電梯的模擬。算法

設計策略

先來先服務的單電梯是一個標準的"生產者-消費者"模型。雖然在本次做業中調度器彷佛是沒必要要的,但爲了更好地應用"生產者-消費者"模型,並方便下一次做業的擴展,仍是應該保留了調度器的概念,將其做爲"托盤"來存放還未服務的請求。編程

顯然,讀取輸入並解析爲請求就成爲了生產者,而電梯就成爲了消費者,而在本次做業中調度器其實只起到了存放共享數據的做用,並無作任何真正意義上的調度。生產者將請求推入位於調度器裏的請求隊列裏,另外一邊電梯按照先來先服務的傻瓜調度算法將請求一個個取出來並執行。爲了保證訪問請求隊列的線程安全性,推入和取出請求的方法必須用synchronized關鍵字上鎖。設計模式

輸入和電梯各是一個線程。當請求隊列爲空時,電梯wait(),直到一個新的請求被推入請求隊列,電梯被喚醒。安全

程序結構

本次做業結構比較簡單,一共四個類:輸入、調度器、電梯和主類。輸入和電梯兩個線程經過調度器中共享數據的方式進行通訊。多線程

在度量上能夠看出總體比較均衡,沒有出現個別方法複雜度過大的狀況。架構

關於BUG

記得應該是一遍過了,沒有發現本身程序的bug。性能

菜雞如我也不會測別人的bug,互測摸了。測試

第六次做業

本次做業,須要完成的任務爲單部多線程可捎帶調度(ALS)電梯的模擬。優化

設計策略

此次的電梯依然只有一個,但要求支持捎帶。要實現捎帶,就涉及到主請求捎帶請求這兩個概念。當電梯處於空閒狀態時,嘗試從請求隊列中取出一個請求,做爲主請求。在該請求被完成以前,電梯運行路徑上遇到的全部目標方向與電梯運行方向相同的請求均被做爲捎帶請求,存入位於調度器中的捎帶請求隊列中。當主請求被完成後,從捎帶請求隊列中取出一個請求,成爲新的主請求。編碼

以上取請求的操做依然遵循先來先服務的原則,但有了捎帶功能後,某些請求能夠被順帶提早知足,電梯的運送效率獲得了極大的提高。

設計模式上仍然採用"生產者-消費者"的基本模型,但此次調度器有了些實際做用,用於管理主請求和捎帶請求之間的轉換關係。

程序結構

因爲仍然是單電梯,僅僅是增長了捎帶功能,而模型沒有變,因此程序結構大致上和上一次相似。

此次代碼寫得比較急,有些該封裝的地方沒封裝,該複用的地方沒複用,因此致使某些方法比較冗長。好比電梯線程的run()方法中,將細節都暴露了出來,有很多重複的語句,如今我本身理解起來都有些吃力。有時間仍是應該把代碼風格優化一下。

關於BUG

完全崩盤。要怪只能怪沒有作好足夠的本地測試(其實也不會測),結果公測和互測都爆炸了,而緣由僅僅是從-1層到1層有個地方忘了處理。就由於這一個小細節,整個做業的努力白費,算是一個很大的教訓了。下次不能再這麼佛系地對待測試,也毫不能對本身的程序抱有莫名的自信。

第七次做業

本次做業,須要完成的任務爲多部多線程智能(SS)調度電梯的模擬。

設計策略

終於來了,傳說中OO做業的難度峯值。

相比前兩次,最主要的區別就是由單電梯變成了多電梯,那麼如何處理三個電梯之間的協做關係就成了主要問題。而事實上,所謂的協做關係也只是保證線程安全性,在這個基礎上採用電梯間相互爭搶的模式,即誰先到達某個請求的出發樓層,誰就服務這個請求。這種策略簡單暴力,調度器不須要分配請求,而效率最後被證實也很好。

在電梯調度算法方面,我採用了LOOK算法,並進行了必定的優化:電梯每達到一層,檢查請求隊列中是否有能夠知足的請求,若無則wait(),如有則根據如今電梯內是否有人分兩種狀況移動:

  1. 電梯內有人,保持原來的運行方向,由下至上再由上至下循環掃描全部能到達的樓層;
  2. 電梯內沒人,若當前方向上沒有能夠知足的請求,則調轉方向,不然保持原方向移動。

經過使用wait()和notifyAll(),電梯在空閒時不會無腦瘋狂調轉方向,從而更貼近真實狀況。

此外,本次做業對電梯增長了更多的限制條件,如轎廂容量、可停靠樓層等。對於單個電梯沒法知足的請求,須要由調度器將其拆分紅兩個請求,而後先把第一個請求放進請求隊列。這裏我利用了HashMap結構,將第一個請求做爲鍵,第二個請求做爲值存儲起來。每當一個請求被完成後,就在HashMap中查找是否有對應的第二個請求,如有就放進請求隊列中。

程序結構

本次做業新增了Request類和Floor類。Request繼承了PersonRequest,以實現請求的拆分從新建立,並加入狀態位status,用來標記某個請求當前正處於未被服務的狀態,仍是已經在某個電梯內部了。Floor做爲電梯的構造參數之一,做用是管理電梯的樓層信息,包括可達樓層、最高最低層、判斷某個樓層是否可達。包括Floor在內的全部電梯屬性均可以在主類中顯式地修改,避免硬編碼,下降耦合度。

我在最後一遍提交前已經把整個程序結構優化了一遍,封裝了不少過程,讓方法長度儘量的均衡,然而如今看來貌似仍是有些差強人意。這個方面還得再下功夫……

關於BUG

此次我深入吸收了上次的教訓,不會測bug就虛心請教大佬,要來了評測姬。正所謂不測不知道,一測嚇一跳。結果果真顯示有嚴重的bug,一個是拆分請求後第二個請求推入時間過早,致使乘客還沒從第一個電梯出來就進了第二個電梯;另外一個問題是某些狀況下電梯wait()後就再也醒不過來了,緣由在於notifyAll()的邏輯不太對。

多線程debug的確比較難受,不過好在我向來就習慣用print大法,在每一個轉向、睡眠、喚醒等關鍵環節都print出相關信息,因此仍是能夠比較快地定位到有問題的代碼片斷。

這樣一來強測點穩妥全過,性能分也不賴。互測也很方便,直接用評測姬跑一遍就行,雖然最後只發現了別人的一處bug,果真A組都是大佬orz。

心得體會

這三次做業層層遞進的,難度也一次次加大,尤爲是第三次的多電梯,對設計架構的要求很高,若是在最開始沒有把各個類之間的關係梳理清楚,那麼功能實現起來就會重重受阻,甚至面臨全盤重構的可能。

然而,這三次做業也是有共性的。對於我來講,輸入推入請求,電梯取出請求,這種"生產者-消費者"模型的核心沒有變。只要能設計好架構,讓耦合度降到最低,那麼任意電梯數量、各類限制要求均可以輕鬆地知足。調度算法也應該封裝起來,想用哪一種算法單獨替換便可,不要由於算法而改變架構自己。

經過這個單元的訓練,我對多線程編程有了深刻的瞭解,掌握了線程間通訊、同步、互斥的方法,保證線程的安全性。更重要的一方面是,設計架構的能力有了不小的提高,明白如何才能設計出高內聚、低耦合的程序,這對之後可能的企業工做將有巨大的幫助。

相關文章
相關標籤/搜索