oo第二單元總結——多線程電梯

前言

        第二單元是咱們學習oo以來第一次接觸多線程。這一單元的三次做業和之前同樣,採用了難度遞進的方式,並且前兩次做業的設計思路在第三次做業都多多少少有些體現(或者說是在其基礎上作出的改進)。因此此次博客將以第三次做業爲主,對這一單元的做業進行分析。算法

設計策略

        第一次做業是傻瓜電梯的設計,我就嚴格按照,先來先服務,一次只有一我的在電梯裏來設計。第二次增長了捎帶請求,也就是說,電梯再也不只能有一我的,而是有一個主乘客(主請求),和多個從屬乘客(從屬請求)。這裏的主請求和第一次做業裏的只有一個請求時候的那一個惟一的請求其實起到的是相同的做用,就是說實際操控電梯的其實是他,而電梯中的其他從屬請求能作的只是在他要進入或者要出去的時候,命令電梯開關門供他進出。固然,從屬請求也有可能成爲控制電梯實際運行的主請求,那就是在主請求已經到達目的地,而從屬請求尚未達到的時候。這兩次做業其實大體上沒多大區別,只是第二次做業在調度方法上作了改進,但整體上看,都是兩個線程(主線程,在我這兒也就是輸入線程,電梯線程)的併發。他們之間的關係與生產者消費者很類似,輸入線程往請求隊列裏投放請求,電梯線程從請求隊列裏拿出請求,這個請求隊列就是兩個線程都會訪問的臨界資源,須要使用synchronized將其鎖住,防止兩個進程同時對其進行讀寫的狀況發生。輸入線程向請求隊列輸入請求是在任什麼時候候均可以進行的,至於電梯線程何時從請求隊列取出請求,那就是調度器的職責了。安全

       接下來具體講一下第三次做業的設計策略。與前兩次做業不一樣,此次做業在之前的基礎上由一部電梯改爲了三部電梯,並且由之前的電梯每一層均可以到達,變成了不一樣電梯能夠到達特定樓層。多線程

       這樣就會出現這種狀況,有一些請求沒法在一部電梯裏完成,而是須要多部電梯配合完成。好比一個從-1樓到3樓的請求,電梯A能夠到-1樓和1樓可是到不了3樓,電梯C能夠到1樓和3樓可是到不了-1樓,這是單獨的A、C都完成不了任務,可是讓A、C合做,A把乘客從-1運到1,再讓把乘客從1運到3,就完成了這個請求。因此一個最容易想到,也最容易實現的方法,就是A、B、C三部電梯各自有本身的請求隊列,在輸入一個請求時,由調度器決定這個請求應該去往哪一個隊列。要是能用一部電梯就解決的請求,那就只用一部電梯,好比-1樓到1樓就用A電梯。若是是一部電梯沒法解決,好比先前提到的-1到3樓的例子,那就把這個請求拆分紅兩個請求,一個是-1到1樓的請求,一個是1到3樓的請求,而後這兩個請求分別進入A、C兩部電梯的等待隊列。這裏還有一個問題,就是1到3樓必須在-1到1樓的請求執行完成以後執行,個人解決方法是每部電梯設置兩個等待隊列,一個是就緒隊列,一個未就緒隊列。就緒隊列和咱們以前的隊列沒有分別。而未就緒隊列中的請求都是從一個主線程輸入的請求拆分紅兩個請求中的一個,而與它對應的另外一個請求在就緒隊列中,只有等就緒隊列中的這個請求已經執行完成後,這個未就緒隊列中的請求才能進入就緒隊列,等待調度器調度給電梯。仍是用-1到3樓的例子來解釋,在這個例子中,我讓1到3樓的請求先到C電梯的未就緒隊列,-1到1樓的請求到A電梯的就緒隊列,這時,調度器能夠再合適的時機把-1到1樓的請求給A電梯,可是1到3樓的請求被分配到C電梯的等待隊列,只有等-1到1樓的請求執行完了也就是說乘客已經從電梯出來,處於1樓了,它才能進入就緒隊列,纔有從1樓又進入C電梯的資格。架構

      這種算法容易實現,可是隻考慮了正確性的問題,它的性能是不好的。好比設想這樣一種狀況:一個從1樓到15樓的請求,調度器把這個請求分給了A電梯,在乘客進入A電梯,電梯關門後,又有一個1到15樓的請求到達請求隊列,調度器又會把這個請求分配給A電梯,那麼A只能把第一個乘客送到15樓,再返回1樓,把第二個乘客送到15樓。1到15樓的請求本來是A、B電梯均可以單獨完成的,可是因爲調度器的調度方式,是隻根據請求自身而不顧電梯的狀態來拆分請求。所以,雖然B電梯也能夠完成這個任務,但因爲調度方式的緣由,明明B沒人用,A處於忙碌狀態,可是調度器認定了A電梯,就一根筋的只把任務分配給A,致使了電梯資源的浪費。併發

度量程序結構

1.代碼統計性能

                  method                                  ev(G)                     iv(G)                       v(G)學習

Dispatcher.Dispatcher() 1.0 1.0 1.0
Dispatcher.geta() 1.0 4.0 8.0
Dispatcher.getb() 1.0 4.0 8.0
Dispatcher.getc() 1.0 4.0 8.0
Dispatcher.getRequestsA() 1.0 1.0 1.0
Dispatcher.getRequestsB() 1.0 1.0 1.0
Dispatcher.getRequestsC() 1.0 1.0 1.0
Dispatcher.getWaita() 1.0 1.0 1.0
Dispatcher.getWaitb() 1.0 1.0 1.0
Dispatcher.getWaitc() 1.0 1.0 1.0
Dispatcher.put(PersonRequest) 1.0 4.0 4.0
Dispatcher.requestsadc() 1.0 2.0 3.0
Dispatcher.setExit() 1.0 1.0 1.0
Dispatcher.sort1(PersonRequest) 1.0 23.0 23.0
Dispatcher.sort2(PersonRequest) 1.0 9.0 9.0
Dispatcher.sort3(PersonRequest) 1.0 6.0 7.0
Dispatcher.waitsadc() 1.0 3.0 3.0
Elevator.arrive() 1.0 19.0 20.0
Elevator.call() 1.0 5.0 5.0
Elevator.close() 1.0 14.0 15.0
Elevator.downfloor() 1.0 1.0 2.0
Elevator.Elevator(Dispatcher,int,int) 1.0 1.0 1.0
Elevator.getFloor() 1.0 1.0 1.0
Elevator.getThread() 1.0 1.0 1.0
Elevator.in(int) 1.0 1.0 1.0
Elevator.move() 1.0 5.0 5.0
Elevator.open() 1.0 2.0 2.0
Elevator.out(int) 1.0 7.0 7.0
Elevator.run() 1.0 6.0 6.0
Elevator.sleep() 1.0 2.0 2.0
Elevator.start() 1.0 2.0 2.0
Elevator.threadsleep(int) 1.0 2.0 2.0
Elevator.upfloor() 1.0 1.0 2.0
Main.main(String[]) 3.0 3.0 3.0
Total 36.0 140.0 158.0
Average 1.0588235294117647 4.117647058823529 4.647058823529412

2.類圖優化

        運行Main類的初始進程便是三部電梯線程的父進程(建立三個電梯進程),同時扮演着輸入線程的角色。他不斷向請求隊列(請求隊列位於調度器中)輸入請求。再有調度器處理這個請求,將處理後的1~2個請求加入到A、B、C的就緒隊列和未就緒隊列中。最後電梯線程執行到適當的時機,調度器會根據狀況把這部電梯的就緒隊列中的請求給到相應電梯來執行,或者將未就緒隊列中的請求移動到就緒隊列中。這種結構好處在於簡單明瞭,只有三個類,分別負責請求輸入,電梯運行和電梯調度,分工明確,思路簡單。可是這種寫法帶來的弊端就是調度器類過於冗長,在寫調度器的時候容易顧此失彼。並且因爲調度器所有都寫在一個類中,修改時要考慮的東西不少,改debug和優化帶來了不小的難度。spa

心得體會

多線程與單線程最大的不一樣在於線程安全問題。單線程你只須要想着你在這一個線程中所要實現的功能,若是要上難度,也只會是在語法上上難度。而多線程不一樣,你必須考慮整體架構,必須考慮線程間的通信,考慮不一樣線程的同步與互斥。因此在設計時,咱們要考慮設計的原則問題:單一責任原則、開放封閉原則、里氏替換原則、接口分離原則、依賴倒置原則。線程

bug分析

  • 發現瞭如下幾種bug,對應解決方案
    • 程序不退出:我最開始的關於程序退出的設計,是在輸入進程的輸入結束時,向調度器發出一個信號end,調度器會在收到了end信號、請求隊列的已經爲空,且電梯中的任務也已經執行完畢在等待新的任務的時候,由電梯進程調用調度器來退出程序。在第一次做業中,當電梯進程因爲等待輸請求而wait的時候,輸入進程向調度器發出end信號,但此時電梯進程還在睡眠,等待輸入進程輸入請求來喚醒,但此時輸入進程已經結束,沒有進程來喚醒電梯進程,也就沒有進程能夠調用已經接收到end信號的調度器來結束程序,這樣電梯進程就永遠處於睡眠狀態,沒法退出。解決方案是讓電梯進程在睡眠的時候,隔一段時間本身醒一次,可是這種喚醒不是由於繼續執行的條件知足而喚醒的,是一種虛假喚醒,會接着進入睡眠狀態(通常的wait都用在while語句中,這裏不詳作說明了),可是再又一次進入睡眠以前,能夠在這個while中加入判斷是否退出程序的語句段,這樣就不存在前面說的問題了。第三次做業中要加入count來計數有幾個電梯正在等待請求,只有在三個電梯都執行完全部任務而且在等待新任務時,才能退出。
    • 調度方法問題:這個主要出如今第三次做業,因爲調度放方法不當,電梯在不應停留的樓層執行了開關門操做。
相關文章
相關標籤/搜索