併發編程實戰學習筆記(三)——基礎構建模塊

同步容器類的問題

  • 整個容器類加鎖,線性訪問容器實例,併發性能非常低
  • 雖然單個操作是線程安全的,但是複合操作如果不另外加鎖,本身無法保證併發安全
  • 迭代器迭代過程中,如果發生元素的操作,會觸發ConcurentModificationException異常,使用了“及時失敗”機制
建議:通過併發容器來代替同步容器,可以極大地提高伸縮性並降低風險

ConcurrentHashMap的優化手段

  • 不是在每個方法上都在同一個鎖上同步並使得每次只能有一線程訪問容器,而是使用一種粒度更細的加鎖機制來實現更大程序的共享,這種機制稱爲分段鎖。這種機制中,任意數量的讀取線程可以併發地訪問Map,執行讀取操作的線程和執行寫入操作的線程可以併發地訪問Map,並且一定數量的寫入線程可以併發地修改Map。
  • 迭代器不會拋出ConcurrentModificationException,因此不需要在迭代過程中對容器加鎖。迭代器具有弱一致性,而並非“及時失敗”。
  • 對於一些需要在整個Map上進行計算的方法,例如size/isEmpty,這些方法的語義被略微減弱了以反映容器的併發特性。
  • 添加了額外的複合原子操作【沒有才插入、映射到值了才移除、映射到舊值才替換、映射到某個值時才替換到新值】

CopyOnWriteArrayList

原理

在每次修改時,都會創建並重新發佈一個新的容器副本,從而實現可變性。

使用場景

每當修改容器時都會複製底層數組,這需要一定的開銷,特別是容器的規模較大時。僅當迭代操作遠遠多於修改操作時,才應該使用“寫入時複製”容器。

阻塞隊列與生產-消費模式

問題

如果生產者生成工作的速率比消費者處理工作的速率快,那麼工作項會在隊列中累積起來,最終耗盡內存內存。

解決方法

阻塞隊列提供了一個offer方法,如果數據項不能被添加到隊列中,那麼將返回一個失敗狀態。這樣你就能創建更多的靈活策略來處理負荷過載的情況,例如將多餘的工作項序列化並寫入磁盤,減少生產者線程的數量,或者通過某種方式來抵制生產者線程。
在構建高可靠的應用程序時,有界隊列是一種強大的資源管理工具:它們能抵制並防止產生過多的工作項,使應用程序在負荷過載的情況下變得更加健壯。

串行線程封閉

優點

對於可變對象,生產者-消費者這種設計與阻塞隊列一起,促進了串行線程封閉,從而將對象所有權從生產者交付給消費者。

線程封閉對象只能由單個線程擁有,通過安全地發佈該對象“轉移”所有權,實現了轉移前由前一線程獨佔,轉移後由後一線程獨佔。

實現方法

阻塞隊列使得這種線程封閉的所有權轉移變得容易,其次還可以通過ConcurrentMap的原子方法remove或者AtomicReference的原子方法compareAndSet來完成這項工作。

閉鎖(CountDownLatch)

使用場景

  • 確保某個計算在其需要的所有資源都被初使化之後才繼續執行。
  • 確保某個服務在其依賴的所有其它服務都已經啓動之後才啓動。
  • 等待直到某個操作的所有參與者(如多玩家遊戲中的所有玩家)都就緒再繼續執行。

使用方法

可以使一個或多個線程等待一組事件發生。閉鎖狀態包括一個計數器,該計數器被初使化爲一個正數,表示需要等待的事件數量。countDown方法遞減計數器,表示一個事件已經發生了,而await方法等待計數器達到零,這表示所有需要等待的事件都已經發生。如果計數器的值非零,那麼await會一直阻塞直到計數器爲零,或者等待中的線程中斷,或者等待超時。

FutureTask也可以用作閉鎖

FutureTask表示的計算是通過callable來實現的,相當於一種可生成結果的Runnable,並且可以處於以下3種狀態:等待運行、正在運行和運行完成。
執行完成表示計算的所有可能結束方式,包括正常結束、由於取消而結束和由於異常而結束等。
Future.get的行爲取決於任務的狀態。如果任務已經完成,那麼get會立即返回結果,否則get會阻塞直到任務進入完成狀態,然後返回結果或者拋出異常。

信號量的作用

計數信號量用來控制同時訪問某個特定資源的操作數量,或者同時執行某個特定操作的數量。
你可以使用Semaphore將任何一種容器變成有界阻塞容器:信號量的計數值會初使化爲容器容量的最大值。add操作在向底層容器添加一個元素之前,首先要獲取一個許可。如果add操作沒有添加任何元素,那麼會立刻釋放許可。同樣,removew操作釋放一個許可,使更多的元素能夠添加到容器中。

柵欄

柵欄與閉鎖的關鍵區別在於,所有線程必須都同時達到柵欄位置,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其它線程。

Exchanger

是一種兩方柵欄,各方在柵欄位置上交換數據。如果一方通過exchange方法申請交換,而另一方還沒有來,則會一直阻塞直至對方也提出申請。

最簡單的方案是,當緩衝區被填滿時,由填充任務進行交換,當緩衝區爲空時,由清空任務進行交換。但是如果新數據的到達率不可預測,那麼一些數據的處理過程就將延遲。另一個方法是,不僅當緩衝被填滿時進行交換,並且當緩衝被填充到一定程序並保持一定時間後,也進行交換。

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