天天看點

鎖11---讀寫鎖(ReentranReadWriteLock&ReadWriteLock)使用詳解

鎖11—讀寫鎖(ReentranReadWriteLock&ReadWriteLock)使用詳解

************ 如有侵權請提示删除 ***************

文章目錄

    • 鎖11---讀寫鎖(ReentranReadWriteLock&ReadWriteLock)使用詳解
      • 【1】基本講解與使用
        • 簡介
        • 使用場景
        • 互斥原則:
        • ReadWriteLock 接口源碼示例
        • 使用示例
      • 【2】ReentrantReadWriteLock源碼分析
        • ReentrantReadWriteLock擁有的特性
          • 擷取順序(公平和非公平)
          • 可重入
          • 鎖降級
          • 鎖擷取的中斷
          • 支援Condition
          • 監控
        • 特性使用執行個體
          • 鎖降級
          • 集合使用場景
      • 【3】Sync、FairSync和NonfairSync
        • Sync的兩個抽象方法
        • FairSync類:
        • NonfairSync 類:
        • Sync的幾個成員變量和靜态内部類
        • 核心方法tryReadLock
        • 核心方法tryWriteLock
      • 【4】ReadLock
      • 【5】WriteLock

【1】基本講解與使用

簡介

ReadWriteLock同Lock一樣也是一個接口,提供了readLock和writeLock兩種鎖的操作機制,一個是隻讀的鎖,一個是寫鎖。

讀鎖可以在沒有寫鎖的時候被多個線程同時持有,寫鎖是獨占的(排他的)。 每次隻能有一個寫線程,但是可以有多個線程并發地讀資料。

所有讀寫鎖的實作必須確定寫操作對讀操作的記憶體影響。換句話說,一個獲得了讀鎖的線程必須能看到前一個釋放的寫鎖所更新的内容。

理論上,讀寫鎖比互斥鎖允許對于共享資料更大程度的并發。與互斥鎖相比,讀寫鎖是否能夠提高性能取決于讀寫資料的頻率、讀取和寫入操作的持續時間、以及讀線程和寫線程之間的競争。

使用場景

假設你的程式中涉及到對一些共享資源的讀和寫操作,且寫操作沒有讀操作那麼頻繁。

例如,最初填充有資料,然後很少修改的集合,同時頻繁搜尋(例如某種目錄)是使用讀寫鎖的理想候選項。

在沒有寫操作的時候,兩個線程同時讀一個資源沒有任何問題,是以應該允許多個線程能在同時讀取共享資源。但是如果有一個線程想去寫這些共享資源,就不應該再有其它線程對該資源進行讀或寫。這就需要一個讀/寫鎖來解決這個問題。

互斥原則:

讀-讀能共存,

讀-寫不能共存,

寫-寫不能共存。

ReadWriteLock 接口源碼示例

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     */
    Lock writeLock();
}
           

使用示例

public class TestReadWriteLock {

    public static void main(String[] args){
        ReadWriteLockDemo rwd = new ReadWriteLockDemo();
		//啟動100個讀線程
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    rwd.get();
                }
            }).start();
        }
        //寫線程
        new Thread(new Runnable() {
            @Override
            public void run() {
                rwd.set((int)(Math.random()*101));
            }
        },"Write").start();
    }
}

class ReadWriteLockDemo{
	//模拟共享資源--Number
    private int number = 0;
	// 實際實作類--ReentrantReadWriteLock,預設非公平模式
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //讀
    public void get(){
    	//使用讀鎖
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+" : "+number);
        }finally {
            readWriteLock.readLock().unlock();
        }
    }
    //寫
    public void set(int number){
        readWriteLock.writeLock().lock();
        try {
            this.number = number;
            System.out.println(Thread.currentThread().getName()+" : "+number);
        }finally {
            readWriteLock.writeLock().unlock();
        }
    }
}

           

首先啟動讀線程,此時number為0;然後某個時刻寫線程修改了共享資源number資料,讀線程再次讀取最新值!

【2】ReentrantReadWriteLock源碼分析

ReentrantReadWriteLock是ReadWriteLock接口的實作類–可重入的讀寫鎖。

ReentrantReadWriteLock擁有的特性

擷取順序(公平和非公平)

ReentrantReadWriteLock不會為鎖定通路強加讀或者寫偏向順序,但是它确實是支援可選的公平政策。

  1. 非公平模式(預設)

    構造為非公平政策(預設值)時,讀寫鎖的入口順序未指定,這取決于可重入性限制。持續競争的非公平鎖可以無限期地延遲一個或多個讀寫器線程,但通常具有比公平鎖更高的吞吐量。

  2. 公平模式

    當構造為公平政策時,線程使用近似的到達順序政策(隊列政策)争奪輸入。當釋放目前持有的鎖時,要麼最長等待的單個寫入線程将被配置設定寫鎖,或者如果有一組讀取線程等待的時間比所有等待的寫入線程都長,那麼該組讀線程組将被配置設定讀鎖。

如果寫入鎖被占有,或者存在等待寫入線程,則試圖擷取公平讀取鎖(非可重入)的線程将阻塞。直到目前等待寫入線程中最老的線程擷取并釋放寫入鎖之後,該線程才會擷取讀取鎖。當然,如果等待的寫入線程放棄等待,剩下一個或多個讀取器線程作為隊列中最長的等待器而沒有寫鎖,那麼這些讀取器将被配置設定讀鎖。

試圖獲得公平寫鎖(非可重入)的線程将阻塞,除非讀鎖和寫鎖都是空閑的(這意味着沒有等待的線程)。(請注意,非阻塞 ReadLock#tryLock()和{@link WriteLock#tryLock()方法不遵守此公平政策設定,并且如果可能的話将立即擷取鎖,而不管等待的線程)。

  1. 構造器源碼如下:
//預設非公平模式
    public ReentrantReadWriteLock() {
        this(false);
    }
     //使用給定的政策建立ReentrantReadWriteLock,true--公平 false-nonfair
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
           

可以看到,預設的構造方法使用的是非公平模式,建立的Sync是NonfairSync對象,然後初始化讀鎖和寫鎖。一旦初始化後,ReadWriteLock接口中的兩個方法就有傳回值了,如下:

public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
           
可重入

這個鎖允許讀線程和寫線程以ReentrantLock的文法重新擷取讀寫鎖。在寫入線程保持的所有寫入鎖被釋放之前,不允許不可重入的讀線程。

另外,寫鎖(寫線程)可以擷取讀鎖,但是不允許讀鎖(讀線程)擷取寫鎖。在其他應用程式中,當對在讀鎖下執行讀取的方法或回調期間保持寫鎖時,可重入性可能非常有用。

鎖降級

可重入特性還允許從寫鎖降級到讀鎖—通過擷取寫鎖,然後擷取讀鎖,然後釋放寫鎖。但是,從讀鎖到寫鎖的更新是不可能的。

鎖擷取的中斷

在讀鎖和寫鎖的擷取過程中支援中斷 。

支援Condition

Condition詳解參考:Condition與Lock使用詳解。

寫鎖提供了Condition實作,ReentrantLock.newCondition;讀鎖不支援Condition。

監控

該類支援确定鎖是否持有或争用的方法。這些方法是為了監視系統狀态而設計的,而不是用于同步控制。

特性使用執行個體

鎖降級
class CachedData {
    Object data;
    //volatile修飾,保持記憶體可見性
    volatile boolean cacheValid;
    //可重入讀寫鎖
    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
 
    void processCachedData() {
    	//首先擷取讀鎖
      rwl.readLock().lock();
      //發現沒有緩存資料則放棄讀鎖,擷取寫鎖
      if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        try {
          // Recheck state because another thread might have
          // acquired write lock and changed state before we did.
          if (!cacheValid) {
            data = ...
            cacheValid = true;
          }
          // Downgrade by acquiring read lock before releasing write lock
          rwl.readLock().lock();
        } finally {
        	//進行鎖降級
          rwl.writeLock().unlock();
           // Unlock write, still hold read
        }
      }
 
      try {
        use(data);
      } finally {
        rwl.readLock().unlock();
      }
    }
  }}

           
集合使用場景

通常可以在集合使用場景中看到ReentrantReadWriteLock的身影。不過隻有在集合比較大,讀操作比寫操作多,操作開銷大于同步開銷的時候才是值得的。

執行個體如下:

class RWDictionary {
	//集合對象
    private final Map<String, Data> m = new TreeMap<String, Data>();
    //讀寫鎖
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    //擷取讀鎖
    private final Lock r = rwl.readLock();
    //擷取寫鎖
    private final Lock w = rwl.writeLock();
 
    public Data get(String key) {
      r.lock();
      try { return m.get(key); }
      finally { r.unlock(); }
    }
    public String[] allKeys() {
      r.lock();
      try { return m.keySet().toArray(); }
      finally { r.unlock(); }
    }
    public Data put(String key, Data value) {
      w.lock();
      try { return m.put(key, value); }
      finally { w.unlock(); }
    }
    public void clear() {
      w.lock();
      try { m.clear(); }
      finally { w.unlock(); }
    }
 }}
           

【3】Sync、FairSync和NonfairSync

再次回顧ReentrantReadWriteLock構造方法:

public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
           

從上面可以看到,構造方法決定了Sync是FairSync還是NonfairSync。Sync繼承了AbstractQueuedSynchronizer,而Sync是一個抽象類,NonfairSync和FairSync繼承了Sync,并重寫了其中的抽象方法。

參考博文:隊列同步器AQS-AbstractQueuedSynchronizer 原理分析

Sync的兩個抽象方法

Sync中提供了很多方法,但是有兩個方法是抽象的,子類必須實作。

// 如果目前線程在試圖擷取讀取鎖時由于線程等待政策而應該阻塞,則傳回true。
 abstract boolean readerShouldBlock();

//如果目前線程在試圖擷取寫鎖時由于線程等待政策而應該阻塞,則傳回true。
 abstract boolean writerShouldBlock();
           

writerShouldBlock和readerShouldBlock方法都表示當有别的線程也在嘗試擷取鎖時,是否應該阻塞。

FairSync類:

static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }

           

對于公平模式,hasQueuedPredecessors()方法表示前面是否有等待線程。一旦前面有等待線程,那麼為了遵循公平,目前線程也就應該被挂起。

NonfairSync 類:

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        final boolean writerShouldBlock() {
            return false; // writers can always barge
        }
        final boolean readerShouldBlock() {
            /* As a heuristic to avoid indefinite writer starvation,
             * block if the thread that momentarily appears to be head
             * of queue, if one exists, is a waiting writer.  This is
             * only a probabilistic effect since a new reader will not
             * block if there is a waiting writer behind other enabled
             * readers that have not yet drained from the queue.
             */
            return apparentlyFirstQueuedIsExclusive();
        }
    }

           

從上面可以看到,非公平模式下,writerShouldBlock直接傳回false,說明不需要阻塞,可以直接擷取鎖。

而readShouldBlock調用了apparentFirstQueuedIsExcluisve()方法。該方法在目前線程是寫鎖占用的線程時,傳回true,否則傳回false。即,如果目前有一個寫線程正在寫,那麼該讀線程應該阻塞。

Sync的幾個成員變量和靜态内部類

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 6317671515068378041L;

        /*
         * Read vs write count extraction constants and functions.
         * Lock state is logically divided into two unsigned shorts:
         * The lower one representing the exclusive (writer) lock hold count,
         * and the upper the shared (reader) hold count.
         */
		
        static final int SHARED_SHIFT   = 16;//共享移位 16
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);//65536
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;//65535
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//65535

        //傳回shared保持的計數-無符号右移
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        //傳回exclusive 保持的計數
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

         //用于每線程讀取保持計數的計數器。作為一個ThreadLocal,緩存在cachedHoldCounter
        static final class HoldCounter {
            int count = 0;
            // Use id, not reference, to avoid garbage retention
            final long tid = getThreadId(Thread.currentThread());
        }

        /**
         * ThreadLocal subclass. Easiest to explicitly define for sake
         * of deserialization mechanics.
         */
        static final class ThreadLocalHoldCounter
            extends ThreadLocal<HoldCounter> {
            public HoldCounter initialValue() {
                return new HoldCounter();
            }
        }
private transient ThreadLocalHoldCounter readHolds;
private transient HoldCounter cachedHoldCounter;
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
           

核心方法tryReadLock

//為讀取執行tryLock,在兩種模式下"駁船"行為都可用。這實際上與tryAcquireShared相同,隻是沒有調用readerShouldBlock。
        final boolean tryReadLock() {
            Thread current = Thread.currentThread();
            for (;;) {
                int c = getState();
                //如果有寫鎖占用,傳回false
                if (exclusiveCount(c) != 0 &&
                    getExclusiveOwnerThread() != current)
                    return false;
                int r = sharedCount(c);
                //判斷是否達到最大值
                if (r == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                    //CAS算法更新
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    if (r == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        HoldCounter rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            cachedHoldCounter = rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                    }
                    return true;
                }
            }
        }
           

核心方法tryWriteLock

//為寫入執行tryLock,在兩種模式下"駁船"行為都可用。這實際上與tryAcquire 相同,隻是沒有調用writerShouldBlock。
        final boolean tryWriteLock() {
            Thread current = Thread.currentThread();
            int c = getState();
            if (c != 0) {
            //擷取持有計數
                int w = exclusiveCount(c);
                // 0 表示沒有擷取到鎖
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                    //已經達到最大值
                if (w == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
            }
            //CAS出了問題
            if (!compareAndSetState(c, c + 1))
                return false;
                //設定目前線程為擁有獨占通路權的線程
            setExclusiveOwnerThread(current);//AQS的setExclusiveOwnerThread
            return true;
        }

           

【4】ReadLock

讀鎖源碼執行個體如下:

public static class ReadLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -5992448646407690164L;
        private final Sync sync;

        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

         //如果寫鎖沒有被其他線程擷取則擷取讀鎖并立即傳回。
        // 如果寫入鎖由另一個線程持有,則目前線程出于線程排程目的而禁用,并處于休眠狀态,直到擷取了讀鎖。
        public void lock() {
            sync.acquireShared(1);//共享模式 使用AQS的acquireShared方法
        }

//擷取讀鎖除非目前線程被中斷;
 //如果寫鎖沒有被其他線程擷取則擷取讀鎖并立即傳回。
// 如果寫入鎖由另一個線程持有,則目前線程出于線程排程目的而禁用,并處于休眠狀态,直到擷取了讀鎖。 
 //如果讀鎖被目前線程擷取,或者被别的線程中斷了目前線程。在上述情況下,如果目前線程 在進入該方法時已經設定了中斷狀态或
 //者在擷取讀鎖時被中斷,則抛出InterruptedException并将目前線程的中斷狀态清除。
 //在該實作中,由于該方法是顯式的中斷點,是以相對于鎖的正常或可重入擷取,優先考慮對中斷作出響應。
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);//同樣調用AQS的acquireSharedInterruptibly
        }

//在調用時寫入鎖不被另一個線程持有時才擷取讀鎖,并立即傳回true。
//即使将此鎖設定為使用公平排序政策,如果鎖可用,則調用{tryLock()}将立即擷取讀取鎖,而不管其他線程目前是否正在等待讀取鎖。
//這種“駁船行為”在某些情況下是有用的,即使它破壞了公平性。
//如果希望遵守此鎖的公平性設定,則使用{@link#tryLock(long,TimeUnit)tryLock(0,TimeUnit.SECONDS)}這幾乎等效(它還檢測中斷)。
//如果寫鎖被其他線程擷取,則立即傳回false。
        public boolean tryLock() {
            return sync.tryReadLock();//Sync的tryReadLock方法
        }
        
//如果寫入鎖在給定的等待時間内沒有被其他線程持有,并且目前線程沒有被中斷,則擷取讀取鎖。
//如果寫鎖沒有被其他線程持有則擷取讀鎖并傳回true。
//如果該鎖已被設定為使用公平排序政策,那麼如果任何其他線程正在等待該鎖,則目前線程不會擷取可用的讀鎖。
//這與tryLock方法形成對比。
//如果想要一個Timed的tryLock在一個公平鎖上,你可以使用如下兩種方式結合在一起:
         * if (lock.tryLock() ||
         *     lock.tryLock(timeout, unit)) {
         *   ...
         * }}
//如果寫鎖被其他線程持有則目前線程由于線程排程目的進入睡眠直到下面事情之一發生:
 * 讀鎖被目前線程擷取;
 * 别的線程中斷了目前線程
 * 指定的等待時間過去。
//如果擷取了讀鎖則傳回true。
 //如果目前線程 在進入該方法時已經設定了中斷狀态或者在擷取讀鎖時被中斷,則抛出InterruptedException并将目前線程的中斷狀态清除。
//如果指定等待時間過去則傳回false。如果時間小于或等于零,則該方法根本不會等待。
//在該實作中,由于該方法是顯式的中斷點,是以優先考慮響應中斷,而不是正常或可重入地擷取鎖及報告等待時間的流逝。
        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));//AQS的tryAcquireSharedNanos
        }

         //試圖釋放鎖,如果讀取器的數量現在是零,那麼寫鎖可以擷取。
        public void unlock() {
            sync.releaseShared(1);//AQS的releaseShared
        }

         //ReadLocks不支援Condition ,會抛出UnsupportedOperationException
        public Condition newCondition() {
            throw new UnsupportedOperationException();
        }

    }
           

【5】WriteLock

WriteLock 源碼執行個體如下:

public static class WriteLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -4992448646407690164L;
        private final Sync sync;

        protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }


//如果讀鎖和寫鎖均未被其他線程持有,則擷取寫鎖并設定寫鎖計數為one。
//如果目前線程已經持有寫鎖,則保持計數遞增1,并且方法立即傳回。
//如果鎖由另一個線程持有,則目前線程出于線程排程目的而禁用,并處于休眠狀态,
//直到擷取了寫鎖,此時将寫鎖保持計數設定為one。       
        public void lock() {
            sync.acquire(1);//這裡使用AQS的acquire方法
        }
//擷取寫鎖除非目前線程被中斷。
//如果讀鎖和寫鎖均未被其他線程持有,則擷取寫鎖并設定寫鎖計數為one。
//如果目前線程已經持有寫鎖,則保持計數遞增1,并且方法立即傳回。       
如果鎖由另一個線程持有,則目前線程出于線程排程目的而禁用,并處于休眠狀态,直到下面事情發生:
* 目前線程擷取了寫鎖;
* 别的線程中斷了目前線程
//如果目前線程在進入這個方法時已經設定了它的中斷狀态或者當擷取寫鎖時被中斷則抛出InterruptedException并清空中斷狀态
//在該實作中,由于該方法是顯式的中斷點,是以相對于鎖的正常或可重入擷取,優先考慮對中斷作出響應。
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);//AQS的acquireInterruptibly
        }

//如果沒有被其他線程在調用該方法時擷取寫鎖,則目前線程擷取寫鎖。
//如果沒有讀鎖或者寫鎖被其他線程持有,則目前線程擷取寫鎖并傳回true且設定寫鎖持有計數為one。
//即使鎖被設定了公平政策,調用tryLock()方法将會立即擷取鎖(如果鎖可用),不管目前是否有其他線程在等待寫鎖。
//這種“駁船行為”在某些情況下是有用的,即使它破壞了公平性。
//如果想保持鎖的公平性,則嘗試使用tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) 二者是等效的。
//如果目前線程已經持有了鎖,則将持有計數+1并傳回true。
//如果鎖被其他線程持有則立即傳回false。
        public boolean tryLock( ) {
            return sync.tryWriteLock();//Sync的tryWriteLock
        }

//擷取寫鎖,如果鎖沒有被其他線程持有在等待時間内并且目前線程沒有被中斷。
//如果沒有讀鎖或者寫鎖被其他線程持有則擷取寫鎖并且傳回true,同時将寫鎖持有計數設定為one。
//如果鎖被設定了公平政策,則可能擷取不到可用的鎖如果有其他的線程在等待這個寫鎖。
//這與tryLock()方法形成了對比。
//如果想要一個定時的tryLock可以使用如下方式結合:
         * if (lock.tryLock() ||
         *     lock.tryLock(timeout, unit)) {
         *   ...
         * }}</pre>
//如果目前線程已經持有鎖,則将持久計數+1,然後傳回true。
//如果鎖被其他線程持有,則出于線程排程目的目前線程将會進入睡眠狀态直到以下三件事情發生之一:
	* 目前線程擷取寫鎖;
	* 别的線程中斷了目前線程;
	* 指定等待時間過去。
//如果目前線程擷取到寫鎖則傳回true并将寫鎖持有計數設定為one。
//如果目前線程在進入該方法時設定了中斷狀态或者在嘗試擷取寫鎖時被中斷,則會抛出InterruptedException并将中斷狀态清空。
//如果指定等待時間過去,則傳回false。如果time小于或者等于0,方法不會再等待。
//在該實作中,由于該方法是顯式的中斷點,是以優先考慮響應中斷,而不是正常或可重入地擷取鎖及報告等待時間的流逝 。
        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));//AQS的tryAcquireNanos
        }

         //試圖釋放鎖。如果目前線程是鎖的持有者,則将持有計數減一。如果持有計數為0,則釋放鎖。
         //如果目前線程不是鎖的持有者,則抛出IllegalMonitorStateException。
        public void unlock() {
            sync.release(1);//AQS的release
        }
        
//傳回Lock執行個體使用的Condition執行個體-寫鎖支援,讀鎖不支援Condition。
//Condition執行個體支援類似于Object螢幕方法如wait/notify/notifyAll的螢幕方法,如Condition.await(),signal(),signalAll().
//當調用Condition方法但是寫鎖沒有被持有則抛出IllegalMonitorStateException。
//(讀取鎖獨立于寫入鎖儲存,是以不會被檢查或受到影響。然而,在目前線程還擷取了讀鎖時,
//調用條件等待方法本質上總是一個錯誤,因為其他可能解鎖的線程将無法擷取寫鎖。)
//當Condition#await()方法被調用時,寫鎖被釋放,并且在它們傳回之前,重新擷取寫鎖,并且鎖保持計數恢複到調用方法時的狀态。
//當一個線程在等待的時候被中斷,則抛出InterruptedException并且将該線程的中斷狀态清空。
//等待線程以FIFO順序被喚醒。      
//對于從等待方法傳回的線程,重新擷取鎖的順序與最初擷取鎖的線程相同,在預設情況下,沒有指定鎖,
//但是對于公平鎖,那些等待時間最長的線程更有利。
        public Condition newCondition() {
            return sync.newCondition();
        }

        public String toString() {
            Thread o = sync.getOwner();
            return super.toString() + ((o == null) ?
                                       "[Unlocked]" :
                                       "[Locked by thread " + o.getName() + "]");
        }

         //查詢寫鎖是否被目前線程持有
        public boolean isHeldByCurrentThread() {
            return sync.isHeldExclusively();//Sync的isHeldExclusively
        }

         //查詢目前線程對這個寫鎖的保持次數。線程對每個未與解鎖操作比對的鎖操作都持有鎖
        public int getHoldCount() {
            return sync.getWriteHoldCount();//Sync的getWriteHoldCount
        }
    }
           

ReentrantReadWriteLock.Sync.getWriteHoldCount源碼如下:

final int getWriteHoldCount() {
   return isHeldExclusively() ? exclusiveCount(getState()) : 0;
}
           

參考:多線程并發之讀寫鎖(ReentranReadWriteLock&ReadWriteLock)使用詳解

繼續閱讀