
現實中有這樣一種場景:對共享資源有讀和寫的操作,且寫操作沒有讀操作那麼頻繁。在沒有寫操作的時候,多個線程同時讀一個資源沒有任何問題,是以應該允許多個線程同時讀取共享資源;但是如果一個線程想去寫這些共享資源,就不應該允許其他線程對該資源進行讀和寫的操作了。
JUC學習筆記(一)https://www.cnblogs.com/lm66/p/15118407.html JUC學習筆記(二)https://www.cnblogs.com/lm66/p/15118813.html JUC學習筆記(三)https://www.cnblogs.com/lm66/p/15118976.html JUC學習筆記(四)https://www.cnblogs.com/lm66/p/15122281.html JUC學習筆記(五)https://www.cnblogs.com/lm66/p/15123516.html JUC學習筆記(六)https://www.cnblogs.com/lm66/p/15124308.html
針對這種場景,JAVA的并發包提供了讀寫鎖 ReentrantReadWriteLock, 它表示兩個鎖,一個是讀操作相關的鎖,稱為共享鎖;一個是寫相關的鎖,稱為排他鎖
(1) 線程進入讀鎖的前提條件:
沒有其他線程的寫鎖
沒有寫請求, 或者有寫請求,但調用線程和持有鎖的線程是同一個(可重入 鎖)。
(2) 線程進入寫鎖的前提條件:
沒有其他線程的讀鎖
讀寫鎖具有以下三個重要的特性:
(1)公平選擇性:支援非公平(預設)和公平的鎖擷取方式,吞吐量還是非公平優于公平。
(2)重進入:讀鎖和寫鎖都支援線程重進入。
(3)鎖降級:遵循擷取寫鎖、擷取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成為讀鎖。
ReentrantReadWriteLock 類的整體結構
可以看到,ReentrantReadWriteLock 實作了 ReadWriteLock 接口,ReadWriteLock 接口定義了擷取讀鎖和寫鎖的規範,具體需要實作類去實作;同時其還實作了 Serializable 接口,表示可以進行序列化,在源代碼中可以看到 ReentrantReadWriteLock 實作了自己的序列化邏輯。
場景: 使用 ReentrantReadWriteLock 對一個 hashmap 進行讀和寫操作
線上程持有讀鎖的情況下,該線程不能取得寫鎖(因為擷取寫鎖的時候,如果發 現目前的讀鎖被占用,就馬上擷取失敗,不管讀鎖是不是被目前線程持有)。
線上程持有寫鎖的情況下,該線程可以繼續擷取讀鎖(擷取讀鎖時如果發現寫 鎖被占用,隻有寫鎖沒有被目前線程占用的情況才會擷取失敗)。
原因: 當線程擷取讀鎖的時候,可能有其他線程同時也在持有讀鎖,是以不能把 擷取讀鎖的線程“更新”為寫鎖;而對于獲得寫鎖的線程,它一定獨占了讀寫 鎖,是以可以繼續讓它擷取讀鎖,當它同時擷取了寫鎖和讀鎖後,還可以先釋
放寫鎖繼續持有讀鎖,這樣一個寫鎖就“降級”為了讀鎖。