【JUC】ReentrantReadWriteLock

ReentrantReadWriteLock

概述

先帶着問題去看這個類:

著作權歸https://pdai.tech所有。 鏈接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html

爲什麼有了ReentrantLock還需要ReentrantReadWriteLock? -- 有共享鎖

ReentrantReadWriteLock底層實現原理?

ReentrantReadWriteLock底層讀寫狀態如何設計的? 高16位爲讀鎖,低16位爲寫鎖

讀鎖和寫鎖的最大數量是多少? -- 16位

本地線程計數器ThreadLocalHoldCounter是用來做什麼的?

答:是這裏get就是相當於new了一個對象,用來存放holdCounter的localthread對象

緩存計數器HoldCounter是用來做什麼的?

答:是最後一個獲取到讀鎖的線程計數器,每當有新的線程獲取到讀鎖,這個變量都會更新。當最後一個獲取讀鎖的線程重複獲取讀鎖,或者釋放讀鎖,就會直接使用這個變量,速度更快,相當於緩存。

讀鎖的獲取與釋放是怎麼實現的?

讀鎖實際就是計數++,釋放就是計數-- ,唯一可能影響多線程的地方就是在讀鎖++,--之後進行狀態設置的時候,有個cas操作,可能會有死循環進行操作,那麼如果競爭強度很大的時候,可能一直在循環

寫鎖的獲取與釋放是怎麼實現的?

寫鎖的可以多次上鎖,但是如果是不同的線程進行上鎖,那麼其他線程的上鎖操作就會進入AQS同步隊列

寫鎖如果嘗試上鎖失敗就會掛起線程

釋放:

核心是就是判斷是否當前執行的線程持有了鎖對象(AQS),然後如果是,那麼state減去相應的計數即可

RentrantReadWriteLock爲什麼不支持鎖升級?

什麼是鎖的升降級? RentrantReadWriteLock爲什麼不支持鎖升級?

先要理解什麼是鎖升級和降級:

著作權歸https://pdai.tech所有。 鏈接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html

鎖降級指的是寫鎖降級成爲讀鎖。如果當前線程擁有寫鎖,然後將其釋放,最後再獲取讀鎖,這種分段完成的過程不能稱之爲鎖降級。鎖降級是指把持住(當前擁有的)寫鎖,再獲取到讀鎖,隨後釋放(先前擁有的)寫鎖的過程。

讀鎖和寫鎖的牽制關係是什麼?

當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)

當寫鎖不存在的時候,讀鎖可以多次獲取

  • 重入:此鎖允許reader和writer按照 ReentrantLock 的樣式重新獲取讀取鎖或寫入鎖。在寫入線程保持的所有寫入鎖都已經釋放後,才允許重入reader使用讀取鎖。
    writer可以獲取讀取鎖,但reader不能獲取寫入鎖。
  • 鎖降級:重入還允許從寫入鎖降級爲讀取鎖,實現方式是:先獲取寫入鎖,然後獲取讀取鎖,最後釋放寫入鎖。但是,從讀取鎖升級到寫入鎖是不可能的。
  • 鎖獲取的中斷:讀取鎖和寫入鎖都支持鎖獲取期間的中斷。
  • Condition 支持:寫入鎖提供了一個 Condition 實現,對於寫入鎖來說,該實現的行爲與 ReentrantLock.newCondition() 提供的 Condition 實現對 ReentrantLock 所做的行爲相同。當然,此 Condition 只能用於寫入鎖。
    讀取鎖不支持 ConditionreadLock().newCondition() 會拋出 UnsupportedOperationException
  • 監測:此類支持一些確定是讀取鎖還是寫入鎖的方法。這些方法設計用於監視系統狀態,而不是同步控制。
    對比:

我們發現reentrantReadWriteLock和ReentrantLock類的組成上就差別有讀寫可重入的鎖上多了2個內部類,一個叫readlock一個叫writelock類

這裏讀寫鎖的讀鎖是多線程共享的,即共享鎖。

這裏讀寫鎖的寫鎖是在更改時不允許其他線程操作的,也就是排他鎖。

構造函數

創建函數默認爲不公平鎖方式

public ReentrantReadWriteLock(boolean fair) {
    // 構造函數直接初始化讀鎖和寫鎖,fair使用的是多臺,默認是false
    // 注意這個sync就是對aqs的實現
    sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

// 讀鎖和寫鎖的初始化就是把當前對象設置sync
protected ReadLock(ReentrantReadWriteLock lock) {
    sync = lock.sync;
}

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

內部類分析

java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock#lock

獲取讀鎖

public void lock() {
    // 這裏的sync對象是非公平鎖 --> 注意是reentrantreadwritelock -》 中的NonfairSync
    sync.acquireShared(1);
}

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

// java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared

重點函數分析

讀鎖是如何上鎖的,寫鎖又是如何上鎖的,兩者有什麼差別?

firstReader的目的是什麼,性能如何提升?

爲什麼讀鎖可以共享,寫鎖不可以?

讀鎖和寫鎖兩個如何合作,會不會有衝突?

什麼是鎖降級?

tryAcquireShared讀鎖

java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared

/**
 * 實現父類接口,嘗試獲取共享鎖
 * 返回如果計數成功,那麼就返回1
 *
 * @param unused
 * @return
 */
protected final int tryAcquireShared(int unused) {
	/*
	 * Walkthrough:
	 *
	 * 1. 如果鎖被其他線程持有,則失敗
	 * 2. 否則,這個線程有資格lock這個wrt的狀態,因此ask根據隊列策略是否需要阻塞
	 *    如果不需要,嘗試通過cas修改狀態和更新count來進行授權。
	 *    注意,不檢查是否可重入的狀態哪一步, 它被推遲到完整版,以避免必須檢查保留計數 更典型的非重入情況。
	 * 3. 如果步驟2失敗,因爲線程顯然不合格或CAS失敗或計數飽和導致的,則鏈到版本與完全重試循環。
	 */
	Thread current = Thread.currentThread();
	// 獲取狀態
	int c = getState();
	// 判斷寫鎖的計數不爲0,並且當前持有線程不是自己,那麼獲取讀鎖直接失敗
	// exclusiveCount 獲取低16位數據,如果不爲0,說明有寫獨佔線程
	// sharedCount 計算寫線程數
	// getExclusiveOwnerThread 當前獨佔的線程不是自己
	if (exclusiveCount(c) != 0 &&
		getExclusiveOwnerThread() != current)
		return -1;
	// 統計當前讀鎖個數
	// sharedCount 計算寫線程數
	int r = sharedCount(c);
	/**
	 * 調用當前類中的nonfairsync函數中的數據
	 * {@link NonfairSync#readerShouldBlock} 可以獲取讀鎖
	 * 並且讀鎖數量沒有達到上限
	 * compareAndSetState(c, c + SHARED_UNIT) 高位16 + 1 cas操作 --> 讀鎖(共享鎖)個數加1
	 */
	if (!readerShouldBlock() &&
		r < MAX_COUNT &&
		// cas判斷能否搶佔成功
		compareAndSetState(c, c + SHARED_UNIT)) {
		// r 當前共享單元個數
		if (r == 0) {
			// 設置第一個讀鎖,設置第一個的目的是爲了性能提升?
			// ans: firstReader是獲取讀鎖的第一個線程。如果只有一個線程獲取讀鎖,很明顯,使用這樣一個變量速度更快
			firstReader = current;
			// 並統計當前讀鎖所在的線程累加的次數
			firstReaderHoldCount = 1;
		} else if (firstReader == current) {
			// 如果一直是當前線程進行獲取讀鎖,那麼計數就一直累加
			firstReaderHoldCount++;
		} else {
			/**
			 * cachedHoldCounter 代表的是最後一個獲取讀鎖的線程的計數器。
			 * 這裏get就是相當於new了一個對象
			 * cachedHoldCounter是最後一個獲取到讀鎖的線程計數器,每當有新的線程獲取到讀鎖,這個變量都會更新。
			 * 這個變量的目的是:當最後一個獲取讀鎖的線程重複獲取讀鎖,或者釋放讀鎖,就會直接使用這個變量,速度更快,相當於緩存。
			 * {@link ReentrantReadWriteLock.Sync.ThreadLocalHoldCounter}
			 * {@link HoldCounter}
			 */
			HoldCounter rh = cachedHoldCounter;
			// 如果最後一個線程計數器是 null 或者不是當前線程,那麼就新建一個 HoldCounter 對象
			if (rh == null || rh.tid != getThreadId(current)) {
				// 給當前線程新建一個 HoldCounter
				// readHolds 實際是一個threadlocal -- 這裏主要是更新了緩存
				cachedHoldCounter = rh = readHolds.get();
			} else if (rh.count == 0) {
				// 如果不是 null,且 count 是 0,就將上個線程的 HoldCounter 覆蓋本地的。
				readHolds.set(rh);
			}
			// 對 count 加一
			rh.count++;
		}

		/**
		 * add by xiaof 2022年7月4日09:10:14 如果不考慮緩存,是否可以這樣寫
		 * 後續所有用到的邏輯都要統一一下,這裏主要的目的就是計數,並且前面有做cas操作
		 * {@link ReentrantReadWriteLock.Sync#readCount}
		 */
//                readCount++;
		return 1;
	}
	// 如果cas搶佔不成功/線程滿了,  死循環獲取讀鎖。包含鎖降級策略。
	return fullTryAcquireShared(current);
}
tryAcquire寫鎖獲取

java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquire

protected final boolean tryAcquire(int acquires) {

    Thread current = Thread.currentThread();
    // 獲取計數狀態
    int c = getState();
    // 獲取寫鎖數量,也就是低16位的個數
    int w = exclusiveCount(c);
    // 判斷鎖計數不爲0
    if (c != 0) {
        // (Note: if c != 0 and w == 0 then shared count != 0)
        /**
		 * 只要狀態不爲0,那麼在上鎖的時候就會調用 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法
		 * {@link AbstractQueuedSynchronizer#acquireQueued(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node, int)}
		 */
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        setState(c + acquires);
        return true;
    }
    /**
	 * {@link NonfairSync#writerShouldBlock()}
	 * 如果計數爲0,第一個函數返回永遠爲 false
	 * 第二個是cas操作,吧計數++
	 */
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    // 設置鎖的持有對象爲當前線程
    setExclusiveOwnerThread(current);
    return true;
}
fullTryAcquireShared 獲取讀鎖失敗重試

當存在2個線程競爭獲取寫鎖,然後有一個已經獲取到讀鎖,另外一個讀鎖在同步隊列中的時候觸發

/**
 * Full version of acquire for reads, that handles CAS misses
 * and reentrant reads not dealt with in tryAcquireShared.
 *
 * reads線程滿了,或者cas無法設置成功,如果可重入鎖沒有再tryacquire中處理的情況
 */
final int fullTryAcquireShared(Thread current) {
	/**
	 * {@link Sync#tryAcquireShared(int)}
	 * 這裏部分代碼和tryAcquireShared中的人方法有冗餘,但是整體上更簡單(說白了就是一樣的代碼)
	 */
	HoldCounter rh = null;
	// 這裏是一個自旋重試
	for (;;) {
		// 獲取讀寫鎖狀態數據
		int c = getState();
		// exclusiveCount 獲取寫鎖個數
		if (exclusiveCount(c) != 0) {
			if (getExclusiveOwnerThread() != current)
				return -1;
			// else we hold the exclusive lock; blocking here
			// would cause deadlock.
		} else if (readerShouldBlock()) {
			// Make sure we're not acquiring read lock reentrantly
			// 寫鎖空閒  且  公平策略決定 線程應當被阻塞
			// 下面的處理是說,如果是已獲取讀鎖的線程重入讀鎖時,
			// 即使公平策略指示應當阻塞也不會阻塞。
			// 否則,這也會導致死鎖的。
			if (firstReader == current) {
				// assert firstReaderHoldCount > 0;
			} else { // 當前線程不爲第一個讀線程,上面那個判斷
				if (rh == null) {
					rh = cachedHoldCounter;
					// 計數器爲空或者計數器的tid不爲當前正在運行的線程的tid
					if (rh == null || rh.tid != getThreadId(current)) {
						rh = readHolds.get();
						if (rh.count == 0)
							readHolds.remove();
					}
				}
				if (rh.count == 0)
					return -1;
			}
		}
		// 讀鎖數量達到最多
		if (sharedCount(c) == MAX_COUNT)
			throw new Error("Maximum lock count exceeded");
		if (compareAndSetState(c, c + SHARED_UNIT)) {
			// 申請讀鎖成功,下面的處理跟tryAcquireShared是類似的。
			if (sharedCount(c) == 0) {
				firstReader = current;
				firstReaderHoldCount = 1;
			} else if (firstReader == current) {
				firstReaderHoldCount++;
			} else {
				// 設定最後一次獲取讀鎖的緩存
				if (rh == null)
					rh = cachedHoldCounter;
				if (rh == null || rh.tid != getThreadId(current))
					rh = readHolds.get();
				else if (rh.count == 0)
					readHolds.set(rh);
				rh.count++;
				// 緩存起來用於釋放
				cachedHoldCounter = rh; // cache for release
			}
			return 1;
		}
	}
}

實戰樣例

package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 功能描述
 *
 * @since 2022-06-29
 */
public class Code01_ReadLockMAX {

    public static void main(String[] args) {
        // 讀鎖是什麼,如何上鎖的?
        // 爲什麼說可以共享?如何實現共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        int count = 0;
        // 最大上線 read 第65534次上鎖
        // 第65535次就會報錯
        int MAX = (1 << 16);
        int BREAK = 65535;
        for (int i = 0; i < MAX; i++) {
            if (i == BREAK) {
                System.out.println("break point");
            }
            System.out.println("read 第" + count++ + "次上鎖");
            reentrantReadWriteLock.readLock().lock();
        }
        System.out.println(count);
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-06-29
 */
public class Code02_ReadLockOtherGetReadLock {

    public static void main(String[] args) {
        // 讀鎖是什麼,如何上鎖的?
        // 爲什麼說可以共享?如何實現共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        AtomicInteger count = new AtomicInteger();
        int MAX = (1 << 16);
        int BREAK = 65535;
        // 最大上線 read 第65534次上鎖
        // 第65535次就會報錯
        IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
            if (count.get() == BREAK) {
                System.out.println("break point");
            }
            reentrantReadWriteLock.readLock().lock();
        }).start());
        System.out.println(count.get());
    }

}



package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-07-11
 */
public class Code03_ReadLockLockAndUnlock {

    public static void main(String[] args) {
        // 讀鎖是什麼,如何上鎖的?
        // 爲什麼說可以共享?如何實現共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

        AtomicInteger count = new AtomicInteger();
        int MAX = (1 << 16);
        int BREAK = 65535;
        // 最大上線 read 第65534次上鎖
        // 第65535次就會報錯
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
        if (count.get() == BREAK) {
            System.out.println("break point");
        }
        readLock.lock();
        readLock.lock();
        // 解鎖
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
        readLock.unlock();
        readLock.lock();

        IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
            if (count.get() == BREAK) {
                System.out.println("break point");
            }
            readLock.lock();
            // 解鎖
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
            readLock.unlock();
        }).start());
        System.out.println(count.get());
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-07-11
 */
public class Code04_WriteLockLockAndUnlock {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        // 讀鎖是什麼,如何上鎖的?
        // 爲什麼說可以共享?如何實現共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

//        lockStateNotZeroAndLock(reentrantReadWriteLock);
//        writeLockAndUnlock(reentrantReadWriteLock);
//        otherThreadWriteLock(reentrantReadWriteLock);
        otherThreadWriteLockAndUnlock(reentrantReadWriteLock);

        // 最大上線 read 第65534次上鎖
        // 第65535次就會報錯
//        IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
//            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
//            if (count.get() == BREAK) {
//                System.out.println("break point");
//            }
//            writeLock.lock();
//            // 解鎖
//            System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
//            writeLock.unlock();
//        }).start());
//        System.out.println(count.get());
    }

    public static void lockStateNotZeroAndLock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
        readLock.lock();
        readLock.lock();
        writeLock.lock();
        // 解鎖
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
        writeLock.unlock();
        writeLock.lock();
    }

    public static void writeLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
        writeLock.lock();
        // 解鎖
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解鎖");
        writeLock.unlock();
    }

    public static void otherThreadWriteLock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
        writeLock.lock();
        writeLock.lock();

        // 另外一個線程進行上鎖
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上鎖");
            writeLock.lock();
        }).start());
        System.out.println(count.get());

    }

    public static void otherThreadWriteLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // 另外一個線程進行上鎖
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndIncrement() + "次上鎖");
                writeLock.lock();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int flag = 0;
                IntStream.range(0, flag).forEach(xx -> {
                    System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndDecrement() + "次解鎖:" + xx);
                    writeLock.unlock();
                });
            }
        }).start());

    }

    static int getChoiceWithTimeout(int timeout) {
        Callable<Integer> k = () -> {
            return new Scanner(System.in).nextInt();
        };
        Long start = System.currentTimeMillis();
        int choice = 0;
        boolean valid;
        ExecutorService l = Executors.newFixedThreadPool(1);
        Future<Integer> g;
        g = l.submit(k);
        done: while (System.currentTimeMillis() - start < timeout) {
            do {
                valid = true;
                if (g.isDone()) {
                    try {
                        choice = g.get();
                        break done;
                    } catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
                       e.printStackTrace();
                    }
                }
            } while (!valid);
        }
        g.cancel(true);
        return choice;
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-07-11
 */
public class Code05_WRGetLock {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        // 讀鎖是什麼,如何上鎖的?
        // 爲什麼說可以共享?如何實現共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//        wrgetlockWait(reentrantReadWriteLock);
        readgetlockWait(reentrantReadWriteLock);

    }

    public static void wrgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        writeLock.lock();
        // **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖");

                if (readLock.tryLock(4, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":success");
                } else {
                    System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":fail");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }).start());

        getChoiceWithTimeout(1000 * 100);
    }

    public static void readgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖");

                if (readLock.tryLock(4, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":success");
                } else {
                    System.out.println(Thread.currentThread() + ":在寫鎖存在的情況下獲取讀鎖" + ":fail");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }).start());
    }

    static int getChoiceWithTimeout(int timeout) {
        Callable<Integer> k = () -> {
            return new Scanner(System.in).nextInt();
        };
        Long start = System.currentTimeMillis();
        int choice = 0;
        boolean valid;
        ExecutorService l = Executors.newFixedThreadPool(1);
        Future<Integer> g;
        g = l.submit(k);
        done: while (System.currentTimeMillis() - start < timeout) {
            do {
                valid = true;
                if (g.isDone()) {
                    try {
                        choice = g.get();
                        break done;
                    } catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
                        e.printStackTrace();
                    }
                }
            } while (!valid);
        }
        g.cancel(true);
        return choice;
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 * 鎖降級:重入還允許從寫入鎖降級爲讀取鎖,實現方式是:先獲取寫入鎖,然後獲取讀取鎖,最後釋放寫入鎖。但是,**從讀取鎖升級到寫入鎖是不可能的。**
 *
 * @since 2022-07-11
 */
public class Code06_UpAndDownLock {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        tryDownLock(reentrantReadWriteLock);

    }

    public static void tryDownLock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

        IntStream.range(0, 1).forEach(x -> new Thread(() -> write(readLock, writeLock)).start());
        IntStream.range(0, 1).forEach(x -> new Thread(() -> read(readLock, writeLock)).start());

    }

    public static void read(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {

        System.out.println(Thread.currentThread() + " begin get read lock");
        readLock.lock();

        try {
            System.out.println(Thread.currentThread() + " get read lock, begin execute");
            Thread.sleep(20);
            System.out.println(Thread.currentThread() + " try up read lock as write lock");
            // 讀鎖升級
            writeLock.lock();
            System.out.println(Thread.currentThread() + "  讀鎖升級爲寫鎖成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
            System.out.println(Thread.currentThread() + "  釋放讀鎖");
        }

    }

    public static void write(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {

        System.out.println(Thread.currentThread() + " begin get 寫 lock");
        writeLock.lock();

        try {
            System.out.println(Thread.currentThread() + " get 寫 lock, begin execute");
            Thread.sleep(40);
            System.out.println(Thread.currentThread() + " try up read lock as write lock");
            // 寫鎖降級讀鎖
            readLock.lock();
            System.out.println(Thread.currentThread() + "  寫鎖降級爲讀鎖成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread() + "  釋放寫鎖");
            writeLock.unlock();
            readLock.unlock();
        }

    }

}



package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 * 獲取讀鎖失敗,重試
 *
 * @since 2022-07-11
 */
public class Code07_ReadLockFail {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//        fulltry(reentrantReadWriteLock);
        fulltry2(reentrantReadWriteLock);

    }

    public static void fulltry(ReentrantReadWriteLock reentrantReadWriteLock) {

        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ": 獲取寫鎖");
                writeLock.lock();
                System.out.println(Thread.currentThread() + ": 獲取寫鎖-success");
                System.out.println(Thread.currentThread() + ": 獲取讀鎖");
                readLock.lock();
                System.out.println(Thread.currentThread() + ": 獲取讀鎖-success");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start());

    }

    public static void fulltry2(ReentrantReadWriteLock reentrantReadWriteLock) {

        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // **當寫鎖存在的時候,無法獲取讀鎖(除了當前持有寫鎖的當前線程可以再次獲取)**
        IntStream.range(0, 22).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ": 獲取讀鎖");
                readLock.lock();
                System.out.println(Thread.currentThread() + ": 獲取讀鎖-success");

                int random1 = (int) (Math.random() * 10);
                if (random1 >= 9) {
                    System.out.println(Thread.currentThread() + ": 獲取寫鎖");
                    writeLock.lock();
                    System.out.println(Thread.currentThread() + ": 獲取---------寫鎖-success");
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start());

    }

}


總結

爲什麼讀鎖是共享鎖:
根本原因是state計數的位置不同,共享鎖是int的高位(前16)計數的,而且不會去判斷是否已經有讀鎖,直接計數++即可

爲什麼寫鎖是排他鎖:
state計數的是低位的16位,並且會判斷是否已經存在持有寫鎖,如果有其他線程持有寫鎖,那麼就會進入同步隊列等待

參考

https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html

https://love1024.blog.csdn.net/article/details/80235197

https://blog.csdn.net/fanrenxiang/article/details/104312606

http://ifeve.com/juc-reentrantreadwritelock/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章