Java中讀寫鎖ReadWriteLock

  Java裏面真正意義的鎖並不多,其實真正的實現Lock接口的類就三個,ReentrantLock和ReentrantReadWriteLock的兩個內部類(ReentrantReadWriteLock實現了ReadWriteLock接口,並沒有實現Lock接口,是其內部類ReadLock和WriteLock實現了Lock的接口),其他都是通過我們前面說的一些工具類實現了線程的阻塞。
  這裏寫圖片描述
  前面鎖機制中提到的ReentrantLock 實現了標準的互斥操作,也就是一次只能有一個線程持有鎖,也即所謂獨佔鎖的概念。我們也一直在強調這個特點。顯然這個特點在一定程度上面減低了吞吐量,實際上獨佔鎖是一種保守的鎖策略,在這種情況下任何“讀/讀”,“寫/讀”,“寫/寫”操作都不能同時發生。
  但實際應用場景中我們會經常遇到這樣的情況:某些資源需要併發訪問,並且大部分時間是用來進行讀操作的,寫操作比較少,而鎖是有一定的開銷的,當併發比較大的時候,鎖的開銷就比較可觀了。所以如果可能的話就儘量少用鎖,如果非要用鎖的話就嘗試看能否能實現讀寫分離,將其改造爲讀寫鎖。

使用時注意的幾個方面:
  讀鎖是排寫鎖操作的,讀鎖不排讀鎖操作,多個讀鎖可以併發不阻塞。即在讀鎖獲取後和讀鎖釋放之前,寫鎖並不能被任何線程獲得,
  多個讀鎖同時作用期間,試圖獲取寫鎖的線程都處於等待狀態,當最後一個讀鎖釋放後,試圖獲取寫鎖的線程纔有機會獲取寫鎖。
  寫鎖是排寫鎖、排讀鎖操作的。當一個線程獲取到寫鎖之後,其他試圖獲取寫鎖和試圖獲取讀鎖的線程都處於等待狀態,直到寫鎖被釋放。
寫鎖是可以獲得讀鎖的,即:
      rwl.writeLock().lock();
      //在寫鎖狀態中,可以獲取讀鎖
      rwl.readLock().lock();
      rwl.writeLock().unlock();
  讀鎖是不能夠獲得寫鎖的,如果要加寫鎖,本線程必須釋放所持有的讀鎖,即:
     rwl.readLock().lock();
      //……
      //必須釋放掉讀鎖,才能夠加寫鎖
      rwl.readLock().unlock();
      rwl.writeLock().lock();

記住三點:

1.加了讀鎖,其他線程也能同時讀,但不能寫,寫線程阻塞,必須到所有讀線程釋放讀鎖後寫線程纔會寫,一旦有線程加了寫鎖,所有讀和寫操作的線程都會阻塞,直到有人寫完。
2.一個線程加讀鎖和釋放讀鎖代碼中間不能再有加寫鎖和釋放寫鎖的代碼,不然會有數據不一致性。一個線程的加寫鎖和釋放寫鎖代碼間可以有加讀鎖和釋放讀鎖的操作
3.一個線程加了寫鎖,其他線程什麼都不能幹了,乾等着,但是這個線程內部還是可以加讀鎖的。。。

讀寫鎖應用的場合

我們有時會遇到對同一個內存區域如數組或者鏈表進行多線程讀寫的情況,一般來說有以下幾種處理方式:
1.不加任何限制,多見於讀取寫入都很快的情況,但有時也會出現問題;
2.對讀寫函數都加以同步互斥,這下問題是沒了,但效率也下去了,比如說兩個讀取線程不是非要排隊進入不可;
3.使用讀寫鎖,安全和效率都得到了解決,特別合適讀線程多於寫線程的情況.也就是下面將要展現的模式.

讀寫鎖的意圖

讀寫鎖的本意是分別對讀寫狀態進行互斥區分,有互斥時才加鎖,否則放行.互斥的情況有: 1.讀寫互斥. 2.寫寫互斥. 不互斥的情況是:讀讀,這種情況不該加以限制. 程序就是要讓鎖對象知道當前讀寫狀態,再根據情況對讀寫的線程進行鎖定和解鎖。

小結
當多個線程試圖對同一內容進行讀寫操作時適合使用讀寫鎖。
請理解並記住ReadWriteLock類讀寫鎖的寫法.
讀寫鎖相對於線程互斥的優勢在於高效,它不會對兩個讀線程進行盲目的互斥處理,當讀線程數量多於寫線程尤其如此,當全是寫線程時兩者等效。

參考引用:

兩篇經典:

發佈了405 篇原創文章 · 獲贊 67 · 訪問量 118萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章