多線程設計模式——概述——筆記

主要的是講多線程設計模式。。。其實有代碼的,是java實現版的。。。。不過考慮到我說不定以後不怎麼用java了,所以主要是把每個設計模式都列一個概述,思路總是有用的~~~所以覺得<Java多線程設計模式>這本書還是挺不錯的~~~將每個多線程設計模式都用比較通俗易懂的語言來說明,而且代碼都比較簡單~~~~所以即使是不用java還是可以好好地理解一下多線程的魅力的~~~~

咔咔。。。寫書的人總結得太好了。。。所以我只能上來他的筆記了。。。

1.Single Thread Execution Pattern

——能通過這座橋的,只有一個人

背景:
——多個線程共享一個實例
問題:
——若多個線程都擅自更改實例的狀態,實例會喪失安全性
解決方式:
——首先,找出實例狀態不穩定的範圍(臨界區間),並對臨界區間加以防止同時執行的線程保持在只有一條的情況。
實現:
——java裏面用synchronized
相關:
——當實例的狀態不會改變時,爲了提升throughput,可使用Immutable Pattern。
——想要將引用實例狀態的線程與改變實例狀態的線程拆開,以提高throughput時可使用Read-Write Lock Pattern

093641783.jpg

2.Immutable

——想破壞它也沒辦法

背景:
——多個線程共享一個實例,實例的狀態不會改變
問題:
——使用Single Threaded Execution Pattern,會降低throughput
解決方式:
——當實例建立後狀態就不會再變化時,就要停止使用Single Threaded Execution Pattern了。
——爲了避免失誤造成更改了實例的狀態,故將類寫成無法由線程更改。另外,刪除實例裏所有用來更新狀態的方法(setter)。引用實例狀態的方法(getter)就無妨
實現:
——java中使用private來隱藏字段,此外由於無法確保不可更改,因此還要使用final
關聯:
——對多個線程進行共享互斥,可使用Single Threaded Execution Pattern。
——當修改用的線程數量比用來讀取的線程數量多時,可考慮使用Read-Write Lock


094344815.jpg

3.Guard Suspension

——要等到我準備好喔

背景:
——多個線程共享一個實例
問題:
——若多個線程都擅自更改實例的狀態,實例會喪失安全性
解決方式:
——當實例的狀態不恰當時,就要求線程等待到適合的狀態時。首先,以“警戒條件”來表示實例的“適當的狀態”。並且在進行有安全性疑慮的操作前,都要檢查是否警戒條件滿足。如果警戒條件不成立,就要求線程等待到成立爲止。
——使用Guarded Suspension Pattern,能以警戒條件限制方法的執行。不過,如果警戒條件一直不成立,線程會永遠等待下去,會使程序喪失生命性
實現:
——java中,檢驗警戒條件使用while語句,而要讓線程等待時則使用wait方法。並使用notify/notifyAll通知警戒條件的改變。檢驗、修改警戒條件時,會使用到Single Threaded Execution Pattern
相關:
——當警戒條件不成立時想要馬上退出,就使用BalkingPattern
——Guarded Suspension Pattern中檢驗、更改警戒條件的部分,會使用到Single Threaded Execution Pattern

095302393.jpg

4.Balking

——不需要的話,就算了吧

背景:
——多個線程共享一個實例
問題:
——若多個線程都擅自更改實例的狀態,實例會喪失安全性。可以一直等待安全的時機,又會使程序響應性降低。
解決方式:
——當實例的狀態不適合時,就會中斷掉處理的進行。首先,以“警戒條件”來表示實例的“適當的狀態”,並且在進行有安全性疑慮的操作前,都要檢查是否滿足警戒條件。只有在警戒條件成立時,纔會執行;如果警戒條件不成立,就直接中斷(balk)執行,馬上退出。
實現:
——java語言中,檢驗警戒條件時要使用if語句。當要balk時,可使用return退出,或使用throw拋出異常,檢驗、修改警戒條件時,會使用到Single Threaded Execution Pattern
關聯:
——當想要等到警戒條件成立再執行時,可使用Guarded Suspension Pattern。
——Balking Pattern中檢驗、更改警戒條件的部分,會使用到Single Threaded Execution Pattern

095912625.jpg

5.Producer-Consumer

——我來做,你來用

背景:
——當要從某個線程(Producer參與者)將數據傳給其他線程(Consumer參與者)時
問題:
——當Producer參與者與Consumer參與者處理的速度不同時,速度慢的會扯速度快的後退,而降低程序的throughput。另外,當Producer參與者要寫入數據時,Consumer參與者若同時讀取數據,數據會喪失安全性。
解決方式:
——在Producer參與者與Consumer參與者之間,加上重擊用的Channel參與者。並讓Channel參與者存放多條數據。這樣就可以緩衝Producer參與者與Consumer參與者之間處理速度的差異。另外,只要在Channel參與者裏進行共享互斥,數據就不會喪失安全性。於是throughput可以不降低,又可以在多個線程之間安全地傳送數據。
相關:
——Channel參與者安全傳遞數據的部分,使用了Guarded Suspension Pattern。
——Future Pattern在傳遞返回值時,使用了Producer-Consumer Pattern
——Worker Pattern在傳遞請求時,使用了Producer-Consumer Pattern

100625969.jpg

6.Read-Write Lock

——大家想看就看吧,不過看得時候不能寫喔

背景:
——多條線程共享一個實例,並會有參考實例狀態的線程(Reader參與者),與會改變實例狀體的線程(Writer參與者)
問題:
——若線程之間不進行共享互斥,會喪失安全性。但使用Single Threaded Execution Pattern會使程序throughput降低
解決方式:
——首先,將“控制Reader參與者的鎖定”與“控制Writer參與者的鎖定”分開,假如ReadWriteLock參與者,以提供兩種不同的鎖定。ReadWriteLock參與者會對“Writer參與者—Writer參與者”、“Reader參與者—Writer參與者”進行互斥控制。因爲“Reader參與者-Reader參與者”不會發生衝突,故不會影響安全性,於是不進行共享互斥,這樣可以在不影響安全性的前提下提高throughput
實現:
——java語言可以使用finally快避免忘記解除鎖定。
相關:
——Read-Write Lock Pattern中,ReadWriteLock參與者進行共享互斥的地方,用到了Guarded Suspension Pattern
——完全沒有Writer參與者的時候,可使用Immutable Pattern

101452260.jpg

7.Thread-Per-Message

——這個工作交給你了

背景:
——線程(Client參與者)要調用實例(Host參與者)的方法
問題:
——在方法的屬性處理完之前,控制權不會從Host參與者退出。如果方法的處理屬性花費時間,程序的響應性會降低
解決方式:
——在Host參與者裏,啓動新的線程。並將方法應該進行的工作,交割這個新的線程。這樣Client參與者的線程就可以繼續執行下一個操作了。這樣做,不用更改Client參與者的程序代碼,並能提高程序的響應性。
實現:
——java語言中,爲了簡化啓動線程的程序,可使用匿名內部類
相關:
——想節省啓動線程所花費的時間時,可以使用Worker Thread Pattern,想要將處理的結果返回給Client參與者時,可以使用Future Pattern

214909695.jpg

8.Worker Thread

——等到工作來,來了就工作

別名:
——Thread Pool;Background Thread
背景:
——線程(Client參與者)要調用實例(Host參與者)的方法
問題:
——如果方法的處理屬性很花時間,程序的響應性會降低。爲了提高響應性,而啓動新的線程來處理方法時,啓動線程所花的時間又會降低throughput。另外,當送出的請求太多時,會啓動過多的線程,這會使承載量變差。
解決方法:
——首先,我們事先啓動一些用來進行處理的線程(工人線程)。並將代表請求的實例傳給工人線程。這樣就不需要每次都重新啓動新的線程了。
相關:
——想要獲取工人線程的處理結果時,可以使用Future Pattern
想要將代表請求的實例傳遞給工人線程時,可以使用Producer-Consumer Pattern

214937593.jpg

9.Future

——先給您這張提貨單

背景:
——線程(Client參與者)會將工作委託給其他線程,而Client參與者希望得到處理的結果
問題:
——將工作委託給別人時,如果又等待執行結果,會使響應性降低
解決方式:
——首先,建立一個與處理結果具有相同接口的Future參與者。在處理開始時,先把Future參與者當作返回值返回。處理的結果事後再設置給Future參與者。這樣Clinet參與者就可以在適當的時機,通過Future參與者,獲取(等待)處理的結果。
相關:
等待Client參與者的處理結果時,會使用Guarded Suspension Pattern
Future Pattern可用在Thread-Per-Message Pattern想要獲取處理結果時。
Future Pattern可用在Worker Thread Pattern想要獲取獲取處理結果時。

215002100.jpg

10.Two-Phase Termination

——快把玩具收拾好,去睡覺吧

背景:
——想要結束運行中的程序
問題:
——從外部忽然結束掉線程,會喪失安全性。
解決方式:
——首先,適合進行終止的時機,還是要交給線程自己判斷。所以,定義一個送出“終止請求”的方法用來結束線程:這個方法事實上只會將標識設置爲“受到終止請求”而已,線程要在每個可以開始終止處理的地方自己檢查這個標識。如果檢查的結果爲真,就開始進行終止處理。
實現:
——java語言中,不但要設置受到終止請求的標識,還要使用interrupt方法中斷掉wait、sleep、join的等待狀態。因爲現場到wait、sleep、join拋出InterruptedException以後,就不是中斷狀態了,所以若是使用isInterrupted方法來檢查終止請求,必須特別小心。
爲了在執行時發生異常也能確實進行終止處理,所以要使用finally塊
相關:
——進行終止處理中時,爲了禁止其他操作,可以使用Balking Pattern
爲了確實進行終止處理,使用了Before/After Pattern

215032433.jpg

11.Thread-Specific Storage

——每個線程的保管箱

背景:
——想要將假定在單線程環境下運行的對象(TSObject參與者),在多線程的環境下使用。
問題:
——想要使用TSObject參與者並不簡單。要將TSObject參與者改寫成支持多線程,可能一部小心就丟掉安全性和生命性了。而且,TSObject參與者可能根本不能改寫。而我們也不想改寫使用TSObject參與者的對象(Client參與者)的程序代碼,所以也不想修改TSObject參與者的接口。
解決方式:
——首先,建立一個與TSObject參與者具有相同接口的TSObjectProxy參與者,並建立TSObjectCollection參與者,管理“Client參與者->TSObject參與者”的對照關係
TSObjectProxy參與者會通過TSObjectCollection參與者,獲取當前線程所對應的TSObject參與者,並將工作委託給TSObject參與者。Client參與者會拿TSObjectProxy參與者來代替TSObject參與者使用。
這樣一來,每個TSObject參與者一定只會有特定的一個線程調用他,所以TSObject參與者不需要進行共享互斥,關於多線程的部分,都隱藏在TSObjectCollection參與者裏了。另外,TSObject參與者的接口也不必修改。
不過,使用Thread-Specific Storage Pattern,等於是在程序里加上隱性的context,有程序的可讀性可能變差的危險性。
實現:
——java語言中,使用java.lang.ThreadLocal類擔任TSObjectCollection參與者

215055425.jpg

12.Active Object

——接受異步消息的主動對象

215107863.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章