OO第二次部落格作業
(1)作業分析
三次作業在處理多線程的協同配合時都是使用将同步放在自己寫的“線程安全類”(經測試有些許漏洞_,但是不影響結果就是了);
我個人傾向于把wait()和notify()等操作放在安全類裡面,這樣可以實作邏輯上的抽象,不會使得電梯的工作邏輯比較亂,一開始使用了Java自帶的線程安全類,但是在終止輸入、電梯、排程器時遇到了問題,是以最後還是自己乖乖寫了一個自己的類,把request包裝了一下,僅僅在裡面加入了我自己關于中斷的邏輯;
第一次和第二次都是把電梯線程都放在了main縣線程開啟,在判斷退出時比較醜陋。
第一次作業是通過main函數輪詢判斷隊列情況和電梯運作狀态再通過interupt()函數打斷電梯線程;整體設計上選擇了安穩過活方式,即input不斷将請求放在隊列中,電梯不斷處理請求,一次處理一個,标準的生産者消費者模式!類圖結構如下:

電梯線程,輸入線程和主線程三線程,一個當作緩沖區的queue,于是傻瓜電梯誕生了!
第二次作業是直接使用結束時将null加入到隊列中作為一個結束标志,排程器取到null就會等目前電梯工作結束,再把電梯停止,自己再退出;
并且選擇使用look算法,在每一層都将所有要進入電梯的人和要出去的人都接進來,踢出去。同時為了預測一下第三次作業的多電梯,采用了排程器将請求配置設定給電梯,輸入将請求無腦push到隊列中的做法,希望實作一定的解耦;
對于電梯,我設計了兩個隊列,一個是要進電梯的隊列,一個是要出電梯的隊列;輸入和排程器共用一個隊列,由排程器選擇将合适的請求配置設定給電梯!
整體上的設應該說是邏輯比較清晰也比較穩定的,并沒有在強測和互測中失利(當然這不代表完全沒有漏洞,畢竟個人在設計時還有些不太了解多線程)
第三次的停止思路來自于ppt,再排程器中開了電梯線程,于是當排程器發現沒有請求,并且三個電梯都閑置下來(即沒有請求),這時停止全部線程;然而這裡沒做好,一開始隻是通過判斷隊列情況來stop線程,于是就會有電梯隊列空而“CLOSE-floor-name”沒有輸出,導緻強測痛失一個點!┭┮﹏┭┮,之後改為判斷電梯停止狀态就通過了那個點;
但是,但是,但是,在另一個強測點又翻車了(Σ(っ °Д °;)っ)但是這個問題自己嘗試一直無法複現,于是不知道該如何修複,而第二次送出又神奇的通過了,可能多線程bug便是如此,不經意間出現,不經意間翻車!結構如圖
将ABC三個電梯分别繼承自一個通用的電梯,同時将電梯的樓層抽象出來,電梯的隊列設計還是源于第二次的,排程器和輸入之間有一個緩沖區,排程器選擇将合适的請求給某個電梯,然後這裡就涉及到第三次作業的排程算法了:
為了友善,将請求包裝為了person類
排程器選擇将某個人配置設定給電梯,若可以直達,則直接放在相應電梯的進入清單中等待而不管是否滿員了(這裡是後來的測試表明不太好);若無法直達,則選擇配置設定給可以進入的電梯,判斷次序為ABC,之後再電梯裡會進行送達樓層的選擇;
這裡通過分析,很容易得知這些情況,-3,16-20是A電梯的特殊點;2,4,6,8,10,12,14是B電梯的特殊點;3,5,7,9,11,13是C電梯的特殊點,這裡尤為特殊的是3号樓層,與此配套的便是2号樓層和4号樓層,他們之間的達到比較特殊,需要折返!
當然,由于我把這些判斷都放在了電梯的一個排程算法中,是以對于排程器來說不可見,它隻需要負責自己前面的事情就好!
- 對于A電梯,不能直達的人會選擇在1或者15層下樓;
對于B電梯,不能直達的人送到1或者15層(對于3-2,3-4這種單獨考慮)
對于C電梯,不能直達的,會發現,經過前面的選擇,此時隻有從3層出發的才會到這裡,是以分情況考慮即可,也是選擇了1或者15層
對于3-4(3-2)這種,選擇根據電梯的行進方向關聯起來,便有四種情況;
以上是這次采用的算法,沒有進行更多的優化,因為不太了解助教所說的那種資料結構的包裝是什麼意思,自己的嘗試也發現漏洞百出,便不敢嘗試了,怕影響正确性(隻是思考了優化的思路)。
(2)度量分析及SOLID原則分析
第一次作業:
第一次作業的功能比較簡單,而且算法是傻瓜,是以複雜度不是很高,而且時序圖也不會很複雜;如上圖
在SOLID原則上:
S:類的功能是唯一的
O:僅支援先到先得,傻瓜式排程
L:沒有繼承關系
I:第一次沒有接口
D:無法複用,不太符合原則
第二次作業:
第二次作業的設計是開了三個線程,在複雜度上也做的不是很好,有2個函數的複雜度較高,究其原因是電梯雙隊列的設計導緻的電梯代碼複雜度提升,在邏輯控制上沒有大問題,取消了忙等,采用wait和notify機制,是以cpu時間沒有任何問題,但是需要考慮思索問題,所幸設計目前沒有發現死鎖;
SOLID設計原則:
S:類的功能是仍然是唯一的,但是電梯采用了雙隊列的設計,比較複雜
O:第三次作業成功沿襲(其實是不想重構,感覺設計的還可以)
L:仍未采用繼承關系
I:使用了常數接口,用來共享常數
D:做的還是不夠好,細節不太到位,不過第三次的重用角度看勉強及格
第三次作業:
第三次作業的設計采用了電梯的繼承,分為三個電梯——A、B、C;
在複雜度上有些許不足,電梯的運作邏輯還是有些複雜,内聚有點大,其他的函數幾乎沒有問題;
在時序圖中,主線程負責開啟輸入和排程器線程,而電梯線程都是在排程器中進行啟動,這是為了結束時友善停止電梯,可以直接使用elevatorThread.interupt()函數;排程上電梯會把不能直達的人送回到排程器,是以停止的标志相對于第二次相對複雜一些,也是我出現問題的地方,需要同時滿足四個條件(隊列空,三個電梯等待)才可以停止全部線程。
SOLID原則分析
S:進一步抽象了很多的類出來,但是在功能上保證了唯一性
O:基于第二次的擴充寫法,個人認為具有一定的開放性
L:多個電梯采用繼承自一個基本電梯的思路,而且函數也全部複用或者重寫,基本實作
I:常數接口
D:還是做的不好,排程方面是針對細節程式設計的
(3)bug分析
第一次作業因為比較簡單,是以目前還沒有發現問題;
第二次作業在采用了look算法并且做了較好的解耦後,也沒有bug;
第三次作業有一個bug,在判斷電梯的停止狀态時選擇了使用隊列為空的方式,而使得會出現“CLOSE-floor-name”的語句無法輸出;
在互測過程中發現的bug主要是在第三次作業,其中最多的就是3-2,3-4這類特殊的樓層請求,主要是沒有解決好換方向的問題;
在周五的上機測試中,我個人在課下自己寫時也遇到了一個bug:
我選擇使用PV操作的原理來實作銀行的算法,但是把存取操作都放在了PV操作的外面,即P-存取-V,這種形式,但是因為忽略了OO和OS的差別(或者說是鎖機制和PV操作的差別),在JAVA中,這樣會使得操作不被鎖起來,但是因為邏輯處理不會産生資料沖突,卻有這樣一個問題,多個線程在鎖區域wait等待後釋放鎖,但是因為produce不在鎖裡,是以會出現同時産生一個元素和某個獲得鎖的消費者判斷隊列是否為空,因為隊列不空,于是該消費者不會wait而是直接consume();而produce之後會喚醒一個等待的消費者來消費,卻面臨着隊列中的已經被消費,隊列為空的null指針錯誤;其主要原因是使用if判斷,是以這種wait等待時最好使用while循環判斷,就可以避免這種情況!![]()
OOP第二章部落格
(4)互測思路
講道理(可能這個道理說不通),在有限的時間内看七份多線程代碼,再考慮一下OS等因素,實在是感覺時間有點不夠,而且多線程的了解也相對比較費力,是以沒有評測機的我隻能選擇抱緊大佬大腿,利用評測機測bug感覺也不是很高效(當然OS期中考試也算一個因素),總之感覺互測确實很吃力,有些無從下手的感覺;當然還是嘗試了一下,盡管可能第一次和第二次實在是找不出來放了空🔪,不過在第三次還是找出了幾個bug的
思路總結:
- 我首先選擇使用最基礎的無腦周遊所有單樓層情況,即隻有一條指令,周遊所有情況來檢查基礎正确性。
- 然後就是利用随機生成的資料測試正确性,這裡主要是利用大佬的評測機來檢查正确性,後來在助教和同學的提醒下,自己也思考了如何寫一個Java評測機,但是苦于OS期中放棄了
(5)體會感悟
經過三次作業,對于多線程的感覺就是,沒事不要多線程(真香,但是速度好像确實快_)
好吧,多線程的學習過程恰好與OS的程序線程相逢,感覺就是安排好了(相約多線程!),但是不可否認,多線程給我的感覺就是,有點神奇——多種意義上!
- 這個線程不太穩:多線程的引入對于共用對象互斥通路就有了強制要求,于是需要實作鎖機制來保證線程安全,而且在思路上首先得說得過去,不然必然崩掉......
-
如何有效的停止:我們都知道有線程安全類,但是深入一看,也是鎖,好吧,于是在本次的停止過程中我就不得不自己思考了,因為我發現自己不會直接使用線程安全類停下來,于是還是包裝了;
在搜尋學習後,得知了interupt()中斷大法,但是還得獲得線程對象,有時候還不是很友善!,但是之後發現了ReentrantLock,使用起來友善多了(就是有點遲)
- 其實上周五上機的時候才發現,把鎖當成PV操作使用,OS和OO神奇的聯合起來了,而且還挺好用!!Σ(っ °Д °;)っ進一步想到了管程,但是好像在電梯上使用這個不太友善,反正自己是不知道如何實作,或者說,線程安全隊列就是一個類似于管程的東西??
- 多線程最重要的還是線程安全,否則優化就是搞事情,強行把自己玩沒了(5行修複一個bug,emmm,對于某些神奇之處可能不夠,)而且最神奇的是一個bug的出現機率,因為多線程的随機性,有可能會出現bug 出現的機率較低,這時評測機大多是測不到的(我個人感覺,助教說對于某個案例會有多個測試點,但是我自己都出現了交兩次就過了的情況,實在是不知其中緣由!😭)
好吧,多線程的學習應該才剛剛開始,如何做到有效高并發并且代碼比較合理還有一段路走,但是個人的進步也是有的,我感覺OO學習過程還是痛并快樂着的,畢竟我開着計程車等電梯上樓求導呢!