1. 程式人生 > >Java併發程式設計札記-(四)JUC鎖-07讀寫鎖的升級—StampedLock

Java併發程式設計札記-(四)JUC鎖-07讀寫鎖的升級—StampedLock

StampedLock是JDK1.8新增的一個鎖,是對讀寫鎖ReentrantReadWriteLock的改進。前面已經學習了ReentrantReadWriteLock,我們瞭解到,在共享資料很大,且讀操作遠多於寫操作的情況下,ReentrantReadWriteLock值得一試。但要注意的是,只有當前沒有執行緒持有讀鎖或者寫鎖時才能獲取到寫鎖,這可能會導致寫執行緒發生飢餓現象,即讀執行緒太多導致寫執行緒遲遲競爭不到鎖而一直處於等待狀態。StampedLock可以解決這個問題,解決方法是如果在讀的過程中發生了寫操作,應該重新讀而不是直接阻塞寫執行緒。

StampedLock有三種讀/寫模式:寫、讀、樂觀讀。

  • 寫。獨佔鎖,只有當前沒有執行緒持有讀鎖或者寫鎖時才能獲取到該鎖。方法writeLock()返回一個可用於unlockWrite(long)釋放鎖的方法的戳記。tryWriteLock()提供不計時和定時的版本。
  • 讀。共享鎖,如果當前沒有執行緒持有寫鎖即可獲取該鎖,可以由多個執行緒獲取到該鎖。方法readLock()返回可用於unlockRead(long)釋放鎖的方法的戳記。tryReadLock()也提供不計時和定時的版本。
  • 樂觀讀。方法tryOptimisticRead()僅當鎖定當前未處於寫入模式時,方法才會返回非零戳記。返回戳記後,需要呼叫validate(long stamp)方法驗證戳記是否可用。也就是看當呼叫tryOptimisticRead返回戳記後到到當前時間是否有其他執行緒持有了寫鎖,如果有,返回false,否則返回true,這時就可以使用該鎖了。

此類還支援有條件地提供三種模式轉換的方法。例如,方法tryConvertToWriteLock(long)試圖“升級”模式,如果(1)已經處於書寫模式(2)處於閱讀模式並且沒有其他讀取器或者(3)處於樂觀模式且鎖定可用,則返回有效的寫入標記。這些方法的形式旨在幫助減少在基於重試的設計中發生的一些程式碼膨脹。

StampedLock不是可重入的。

下面是一個StampedLock註釋中的例子。

class Point {
    private double x, y;
    private final StampedLock sl = new StampedLock();
    /**
     * 改變當前座標。
     * 先獲取寫鎖,然後對point座標進行修改,最後釋放鎖。
     * 該鎖是排它鎖,這保證了其他執行緒呼叫move函式時候會被阻塞,直到當前執行緒顯示釋放了該鎖。
     */
void move(double deltaX, double deltaY) { // an exclusively locked method long stamp = sl.writeLock(); try { x += deltaX; y += deltaY; } finally { sl.unlockWrite(stamp); } } /** * 計算當前座標到原點的距離 * * @return */ double distanceFromOrigin() { // A read-only method //1.嘗試獲取樂觀讀鎖,返回stamp long stamp = sl.tryOptimisticRead(); //2.拷貝引數到本地方法棧中 double currentX = x, currentY = y; //3.驗證stamp是否有效 if (!sl.validate(stamp)) { //4.如果stamp無效,說明得到stamp後,又有其他執行緒獲得了寫鎖 //5.獲取讀鎖 stamp = sl.readLock(); try { //6.其他執行緒修改了x,y的值,為了資料的一致性,需要再次再次拷貝引數到本地方法棧中 currentX = x; currentY = y; } finally { //7.釋放讀鎖 sl.unlockRead(stamp); } } //8.使用引數的拷貝來計算當前座標到原點的距離。無論步驟3中stamp有沒有驗證成功,引數的拷貝都是當前座標的值 return Math.sqrt(currentX * currentX + currentY * currentY); } /** * 如果當前座標為原點則移動到指定的位置 */ void moveIfAtOrigin(double newX, double newY) { // upgrade // 獲取讀鎖,保證其他執行緒不能獲取到寫鎖 long stamp = sl.readLock(); try { //如果當前座標為原點 while (x == 0.0 && y == 0.0) { //嘗試升級成寫鎖 long ws = sl.tryConvertToWriteLock(stamp); //如果升級成功,更新座標值 if (ws != 0L) { stamp = ws; x = newX; y = newY; break; } else {//如果升級成功 sl.unlockRead(stamp);//先釋放讀鎖 stamp = sl.writeLock();//再獲取寫鎖 //迴圈while中的操作,直到成功更新座標值 } } } finally { //最後釋放寫鎖 sl.unlock(stamp); } } }

本文就講到這裡,想了解Java併發程式設計更多內容請參考:

相關推薦

Java併發程式設計札記-()JUC-07升級StampedLock

StampedLock是JDK1.8新增的一個鎖,是對讀寫鎖ReentrantReadWriteLock的改進。前面已經學習了ReentrantReadWriteLock,我們瞭解到,在共享資料很大,且讀操作遠多於寫操作的情況下,ReentrantReadWri

Java併發程式設計札記-()JUC-08CountDownLatch

CountDownLatch是一個通用同步器,用於同步一個或多個任務。在完成一組正在其他執行緒中執行的任務之前,它允許一個或多個執行緒一直等待。 可以用一個初始計數值來初始化CountDownLatch物件,任何在這個物件上呼叫wait()的方法都將阻塞,直至

Java併發程式設計札記-()JUC-04Condition簡介

我們已經學習瞭如何通過使用鎖來同步兩個任務,但為了解決某個問題,任務之間只有互斥是不夠的,還需要相互通訊,相互協作。今天就來學習如何實現任務之間的協作。 初識Condition 在任務協作中,關鍵問題是任務之間的通訊。握手可以通過Object的監視器方法(w

Java併發程式設計札記-()JUC-01概述

今天來學習JUC鎖。JUC鎖位於java.util.concurrent.locks包下,為鎖和等待條件提供一個框架,它不同於內建同步和監視器。 參考JDK1.8的java.util.concurrent.locks包,畫出如下圖: CountDown

Java併發程式設計札記-()JUC-10Semaphore簡介

一般的鎖在任意時刻只允許一個執行緒訪問一項資源,而計數訊號量允許n個任務同時訪問一項資源。我們可以將訊號量看做一個許可集,可以向執行緒分發使用資源的許可證。獲得資源前,執行緒呼叫acquire()從許可集中獲取許可。該執行緒結束後,通過release()將許可還

Java併發程式設計札記-()JUC-02Lock與ReentrantLock

今天學習Lock與ReentrantLock。 Java中的鎖有兩種,synchronized與Lock。因為使用synchronized並不需要顯示地加鎖與解鎖,所以往往稱synchronized為隱式鎖,而使用Lock時則相反,所以一般稱Lock為顯示鎖。

Java併發程式設計札記-()JUC-09CyclicBarrier

CyclicBarrier允許一組執行緒互相等待,直到到達某個公共屏障點。如果你希望一組並行的任務在下個步驟之前相互等待,直到所有的任務都完成了下個步驟前的所有操作,才繼續向前執行,那麼CyclicBarrier很合適。 函式列表 //構造方法摘要 Cy

【搞定Java併發程式設計】第20篇: --- ReentrantReadWriteLock詳解

上一篇:重入鎖 --- ReentrantLock 詳解(點選檢視) 本文目錄: 1、讀寫鎖的概述 2、讀寫鎖的具體實現 2.1、讀寫狀態的設計 2.2、鎖的獲取 2.2.1、寫鎖的獲取 2.2.2、讀鎖的獲取 2.3、鎖的釋放 2.3.1、寫鎖的釋放 2.3

Java併發程式設計札記-(六)JUC執行緒池-01概述

前面的例子中總是需要執行緒時就建立,不需要就銷燬它。但頻繁建立和銷燬執行緒是很耗資源的,在併發量較高的情況下頻繁建立和銷燬執行緒會降低系統的效率。執行緒池可以通過重複利用已建立的執行緒降低執行緒建立和銷

Java併發程式設計札記-(五)JUC容器-05ArrayBlockingQueue與LinkedBlockingQueue

今天來學習ArrayBlockingQueue與LinkedBlockingQueue。 ArrayBlockingQueue是一個基於陣列的有界阻塞佇列。“有界”表示陣列容量是固定的。這是一個典型的“有界快取區”,固定大小的陣列在其中保持生產者插入的元素和使

Java併發程式設計札記-(二)JUC概述

從今天開始學習JUC。JUC是java.util.concurrent包的簡稱。下圖是JUC的整體結構。 atomic 以下是JUC中的原子類。 locks 以下是JUC中的鎖,也稱顯示

Java併發程式設計札記-(三)JUC原子類-07CAS

CAS,即compare and swap,比較並交換。CAS操作包含三個運算元:記憶體值(V),預期值(A)、新值(B)。如果記憶體值與預期值相同,就將記憶體值修改為新值,否則不做任何操作。 java.util.concurrent.atomic是建立在CA

Java併發程式設計札記-(五)JUC容器-03ConcurrentHashMap

今天來學習ConcurrentHashMap在JDK1.8中的實現。相比JDK1.7,JDK1.8中ConcurrentHashMap的實現有很大的不同。 結構 先來看下JDK1.7與JDK1.8中ConcurrentHashMap結構的不同。 JDK1.

Java併發程式設計札記-(三)JUC原子類-05原子方式更新類的指定volatile欄位

AtomicReferenceFieldUpdater、AtomicIntegerFieldUpdater和AtomicLongFieldUpdater是基於反射的實用工具,可以提供對關聯欄位型別的訪問。例如AtomicLongFieldUpdater可以對指定

Java併發程式設計札記-(三)JUC原子類-06JDK1.8新增:LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator

DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder是JDK1.8新增的部分,是對AtomicLong等類的改進。比如LongAccumulator與LongAdder在高併發環境下比AtomicLong

Java併發程式設計(10)-顯式的使用

文章目錄 一、顯式鎖 1.1、什麼是顯式鎖 1.2、Lock和ReentrantLock 1.3、如何使用顯示鎖 二、讀寫鎖 2.1、為什麼使用讀寫鎖

Java併發程式設計)volatile關鍵字

  一、volatile特性 volatile的兩點特性:禁止重排序、保證記憶體可見性。volatile不能保證原子性。 1、禁止指令重排 原理:volatile關鍵字通過提供記憶體屏障的方式來防止指令被重排序,編譯器在生成位元組碼時,會在指令序列中插入記憶體屏障來禁止特定

Java併發程式設計)-Lock

Lock         Lock是java 1.5中引入的執行緒同步工具,它主要用於多執行緒下共享資源的控制。本質上Lock僅僅是一個介面(位於原始碼包中的java\util\concurrent\locks中),它包含以下方法 //嘗試獲取鎖,獲取成功則返回

【搞定Java併發程式設計】第19篇:重入 --- ReentrantLock 詳解

AQS系列文章: 1、佇列同步器AQS原始碼分析之概要分析 2、佇列同步器AQS原始碼分析之獨佔模式 3、佇列同步器AQS原始碼分析之共享模式 4、佇列同步器AQS原始碼分析之Condition介面、等待佇列 先推薦兩篇好文章: 1、深入剖析基於併

Java併發程式設計札記-(一)基礎-01基本概念

在學習Java併發程式設計之前,先來了解一下幾個概念。 什麼是併發?維基百科中這樣介紹: 在電腦科學中,併發性是指程式,演算法或問題的不同部分或單元按無序或部分順序執行而不影響最終結果的能力。這允許並行單元的並行執行,這可以顯著提高在多處理器和多核系統