03.29 JAVA高併發編程中的鎖應用

前面文章我們引入了一個話題:《分佈式鎖服務的思考》,今天我們從最基本的鎖概念上開始,慢慢實現一個分佈式鎖服務,希望大家能學到知識。

JAVA高併發編程中的鎖應用

在多線程編程中,我們如果使用到了共享資源或數據,往往是需要考慮是否線程安全的問題。這裡我們先不考慮天生為多線程服務的ConcurrentMap、BlockingQueue。本文介紹下JAVA中的locks包中的鎖有哪些種類,我們編程上該如何選擇?下面是目錄:

  1. JAVA中的Lock

  2. 可重入鎖(ReentrantLock)

  3. 讀寫鎖(ReadWriteLock)

1. JAVA的Lock

在Java Application的編程實踐中,我們會使用到多線程,這裡就會牽扯到共享資源的保護。當然有些資源本身就有線程安全的保護,例如使用ConcurrentMap,BlockQueue等,天生為多線程服務的。

通常情況下,只有獲得lock的線程才具有對共享資源的訪問權限,但是有些鎖允許併發訪問共享資源,例如:ReadWriteLock。線程訪問共享資源,都需要嘗試獲得鎖才可以,java中的Lock實例只是一個普通的java對象,通過lock()就可以獲得鎖。

Lock具有比Synchronized更實用的方法,可以靈活設計程序。使用同步鎖要求加鎖和釋放鎖的過程要相反,同步鎖比較難以應對靈活多變的需求。例如:業務邏輯A的執行需要鎖定B、C,執行B的時候又需要鎖定D,然後可以釋放B、D,這裡的鎖定和釋放不要求順序,隨意加鎖和釋放,最重要的是加鎖和釋放鎖不需要在同一個業務塊中實現。

示意代碼:

JAVA高併發編程中的鎖應用

2. 可重入鎖(ReentrantLock)

最簡單的解釋就是:線程A的業務對共享資源加鎖之後,A線程內部又一個業務又需要操作該資源,那麼A線程可以直接獲得鎖,不需要排隊等待。這就是可重入鎖的解釋,就是這麼簡單。注意,可重入鎖有一些特徵比概念更重要,看下面構造方法有個公平參數:

JAVA高併發編程中的鎖應用

這就有公平和非公平之分了,有些人喜歡總結,稱之為公平鎖和非公平鎖。公平鎖是指多個線程按照申請鎖的順序來獲取鎖,非公平鎖是指多個線程獲取鎖的順序並不是按照申請鎖的順序,所以有可能造成線程飢餓的現象。非公平鎖具有高吞吐量的特點,在業務中根據情況選擇合適的使用。JDK文檔中說:公平的可重入鎖不參與CPU的線程調度,所以可能會有某個線程連續多次獲得獲得鎖,儘管其他線程也是active的狀態,但是CPU未分配時間片。

示例代碼:

JAVA高併發編程中的鎖應用

3. 讀寫鎖(ReadWriteLock)

一個讀寫鎖是一對鎖:一個只讀鎖和一個寫鎖。當資源沒有加寫鎖的時候,讀鎖可以同時被多個讀線程共享。讀寫鎖允許一個線程寫共享數據,而多個讀線程獲得讀鎖之後併發訪問這個共享數據。

通常情況下,讀寫鎖具有更高的訪問併發性,但這也需要根據讀和寫的頻率,以及同一時刻有多少讀和寫而定。例如:數據讀多寫少的情況下使用讀寫鎖就很合適。相反,如果寫操作過多,會花費大量的時間在加鎖的操作上,因此對併發並沒有多大的性能提升。這裡大家可以結合壓力測試和性能分析,確定自己的程序是否應該使用讀寫鎖。

在使用讀寫鎖的時候需要考慮的事項,這也是讀寫鎖實現需要考慮的原則:

  • 確定讀寫優先級,在寫少讀多的情況下,讀優先級比寫的高,也可以使用排隊公平競爭鎖。

  • 確定讀鎖是否需要讓步於寫鎖,不讓的話,寫可能一直不能進行。讓,會減少併發。

  • 確定鎖是否可重入。(ReentrantReadWriteLock)

  • 確定是否允許鎖的降級和提升,例如寫鎖降級成讀鎖,讀鎖提升為寫鎖鎖。

讀寫鎖在使用,JDK提供了ReentrantReadWriteLock類,實現了讀寫鎖並加入了可重入的特性。

  • 具有可重入鎖的公平和非公平模式。(默認參數是非公平的,具有較高的性能)

  • 可重入的特性。寫可重入讀,但讀不可重入寫。所有寫鎖釋放之前,讀不可重入。

  • 鎖降級。讀鎖可降級成寫鎖,在釋放之前先降級成讀鎖,然後釋放。但讀鎖不可提升成為寫鎖。

  • 可中斷。在獲得鎖的過程中可以中斷。

  • 條件的支持。(Condition)

  • 可監控,具有監控手段。

下面是JDK中給出的經典示例,在更新緩存數據之後,對鎖降級:

JAVA高併發編程中的鎖應用

另外一個例子是對Collection進行讀多寫少的場景:

JAVA高併發編程中的鎖應用

總結:

  • Lock提供了比Sync靈活的程序設計思路,程序實現更優雅。

  • 兩個比較重要的鎖實現類:具有公平和非公平模式的可重入鎖以及讀寫鎖。

  • 應用中可以結合壓測性能分析確定選擇的鎖是否合適。

感謝您的閱讀,今天是基礎鋪墊,我計劃最近帶領大家寫一個數據採集的併發程序,具有任務隊列、任務優先級、任務可拆分等功能。


分享到:


相關文章: