Lock顯示鎖實現類及實例

標準 JDK中提供了多種顯示鎖的實現,將在下面的章節中進行介紹。安全

一.ReentrantLock多線程

ReentrantLock 類是一個互斥鎖,它和 synchronized 關鍵字訪問的隱式鎖具備相同的功能,但它具備擴展功能。它也實現了可重入的功能。併發

如何使用 ReentrantLock性能

鎖經過 lock() 獲取,經過 unlock() 釋放,將代碼封裝到 try/finally 塊中是很是重要的,以確保在出現異常的時候也能釋放鎖。這個方法和使用關鍵字 synchronized 修飾的方法是同樣是線程安全的。若是一個線程已經得到了鎖,後續線程調用 lock() 會暫停線程,直到鎖被釋放,永遠只有一個線程能獲取鎖。ui

lock 支持更細粒度的去控制一個方法的同步,以下面的代碼:線程

當第一個任務獲取鎖時,第二個任務獲取鎖的狀態信息:資源

做爲 lock() 方法的替代方法 tryLock() 嘗試去獲取鎖而不暫停當前線程,必須使用 boolean 結果去判斷是否真的獲取到了鎖。同步

二.ReadWriteLockit

讀寫鎖意思就是:讀讀共享,讀寫互斥,寫寫互斥。意思是若是兩個方法都是調用讀鎖,那麼多線程能夠併發訪問。可是一個方法調用讀方法,一個調用寫方法,那麼該鎖就會變成同步鎖(一個方法完了纔去執行另外一個)。變量

ReadWriteLock 指定了另外一種類型的鎖,即讀寫鎖。讀寫鎖實現的邏輯是,當沒有線程在寫這個變量時,其餘的線程能夠讀取這個變量,因此就是當沒有線程持有寫鎖時,讀鎖就能夠被全部的線程持有。若是讀取比寫更頻繁,這將增長系統的性能和吞吐量。

上面的例子首先獲取一個寫入鎖,在 sleep 1秒後在 map 中寫入值,在這個任務完成以前,還有兩個任務正在提交,試圖從 map 讀取值:

當執行上面的代碼時,你會注意到兩人讀取的任務必須等待直到寫入完成(當在讀取的時候,寫是不能獲取鎖的)。寫入鎖釋放後,兩個任務並行執行,它們沒必要等待對方是否完成,由於只要沒有線程持有寫入鎖,它們就能夠同時持有讀取鎖。

三.StampedLock

Java 8 提供了一種新類型的鎖 StampedLock,像上面的例子同樣它也支持讀寫鎖,與 ReadWriteLock 不一樣的是,StampedLock 的鎖定方法返回一個 long 值,能夠利用這個值檢查是否釋放鎖和鎖仍然有效。另外 StampedLock 支持另一種稱爲樂觀鎖的模式。

下面使用 StampedLock 來替換 ReadWriteLock

經過 readLock() 和 writeLock() 方法來獲取讀寫鎖會返回一個稍後用於在 finally 塊中釋放鎖的值。注意,這裏的鎖不是可重入的。每次鎖定都會返回一個新的值,並在沒有鎖的狀況下阻塞,在使用的時候要注意不要死鎖。

就像前面 ReadWriteLock 中的示例同樣,兩個讀取任務必須等待寫入任務釋放鎖。而後同時並行執行打印結果到控制檯。

下面的例子演示了樂觀鎖

經過調用 tryOptimisticRead() 來獲取樂觀讀寫鎖,tryOptimisticRead()老是返回一個值,而不會阻塞當前線程,也不關鎖是否可用。若是有一個寫鎖激活則返回0。能夠經過 lock.validate(stamp) 來檢查返回的標記(long 值)是否有效。

執行上面的代碼輸出:

所以,在使用樂觀鎖時,必須在每次訪問任何共享的變量後驗證鎖,以確保讀取仍然有效

有時將讀鎖轉換爲寫鎖並不須要再次解鎖和鎖定是有用的。StampedLock 爲此提供了tryConvertToWriteLock() 方法,以下面的示例所示:

該任務首先得到一個讀鎖,並將當前的變量計數值打印到控制檯。 可是,若是當前值爲 0,咱們要分配一個新的值23。咱們首先必須將讀鎖轉換爲寫鎖,以不打破其餘線程的潛在併發訪問。 調用 tryConvertToWriteLock() 不會阻塞,但可能會返回 0,指示當前沒有寫鎖定可用。 在這種狀況下,咱們調用writeLock()來阻塞當前線程,直到寫鎖可用。

四.Semaphores

除了鎖以外,併發API還支持計數信號量。 鎖一般授予對變量或資源的獨佔訪問權,而信號量則可以維護整套許可證。 在不一樣的狀況下,必須限制對應用程序某些部分的併發訪問量。

下面是一個如何限制對長時間任務的訪問的例子:

執行程序能夠同時運行10個任務,可是咱們使用5信號量,所以限制併發訪問爲5個。使用try/finally塊,即便在異常的狀況下正確釋放信號量也是很是重要的。

運行上面的代碼輸出:

當有 5 個任務獲取型號量後,隨後的任務便不能獲取信號量了。可是若是前面 5 的任務執行完成,finally 塊釋放了型號量,隨後的線程就能夠獲取星號量了,總數不會超過5個。這裏調用 tryAcquire() 獲取型號量設置了超時時間1秒,意味着當線程獲取信號量失敗後能夠阻塞等待1秒再獲取。

相關文章
相關標籤/搜索