【線程】ReentrantReadWriteLock 內部共享鎖與排他鎖源碼剖析 (十一)

我的原則:先會用再說,內部慢慢來。
學以致用,根據場景學源碼


一、概念

  1. 共享鎖(SHARED): 讀操作相關的鎖。我正在read,任何人都可以 read。
  2. 排他鎖(EXCLUSIVE): write 操作相關的鎖,我正在write,別人不能write,不能read(避免不同的人讀到的信息不一樣)

二、ReentrantReadWriteLock 整體架構

在這裏插入圖片描述

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    /** 讀鎖 */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** 寫鎖 */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    
    final Sync sync;

    /** 默認非公平鎖 **/
    public ReentrantReadWriteLock() {
        this(false);
    }

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

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


    abstract static class Sync extends AbstractQueuedSynchronizer {
    	private static final long serialVersionUID = 6317671515068378041L;
    	// 高16位是 read鎖,低16位是 write 鎖
        static final int SHARED_SHIFT   = 16;
        // read 鎖的單位, write在低16位,所以單位是1,read鎖在高16位,所以單位是 1 * 2 ^ 16
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        // read 鎖最大的數量
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        // write 鎖最大的數量
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
        // 獲取當前共享鎖的數量,也就是 read 鎖的數量。 直接右移 16 位,剩下的就是 read 鎖的數量了
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        /*  
         *  
         *  獲取當前獨佔鎖的數量,也就是 write 鎖的數量 ,抹掉前面16位
         *	二進制運算: n & ( 2 ^ m - 1) == n mod (2 ^ m)
         *  比如: 7  & ( 2 ^ 2 -1 ) = 7 mod (2 ^ 2) = 3 
         *  也就是取餘數操作可以用與運算來解決。
         */
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

		// 計數器
        static final class HoldCounter {
        	// 某個讀線程重入的次數
            int count = 0;
            // tid表示該線程的tid字段的值
            final long tid = getThreadId(Thread.currentThread());
        }

        // 本地線程計數器
        static final class ThreadLocalHoldCounter
            extends ThreadLocal<HoldCounter> {
            // 重寫初始化方法,在沒有進行set的情況下,獲取的都是該HoldCounter值	
            public HoldCounter initialValue() {
                return new HoldCounter();
            }
        }

        private transient ThreadLocalHoldCounter readHolds;
        private transient HoldCounter cachedHoldCounter;
       
        private transient Thread firstReader = null;
        private transient int firstReaderHoldCount;

        Sync() {
            readHolds = new ThreadLocalHoldCounter();
            setState(getState()); // ensures visibility of readHolds
        }
    }

    static final class NonfairSync extends Sync {}
    static final class FairSync extends Sync {}
    public static class ReadLock implements Lock, java.io.Serializable {}
    public static class WriteLock implements Lock, java.io.Serializable {}
    ...

}
  • 讀寫狀態的設計
    在這裏插入圖片描述
假設當前同步狀態值爲S,get和set的操作如下:
(1)獲取寫狀態:
   		 S&0x0000FFFF:將高16位全部抹去

(2)獲取讀狀態:
  	  	S>>>16:無符號補0,右移16位

(3)寫狀態加1:
    	 S+1

(4)讀狀態加1:
  S+(1<<16)即S + 0x00010000

在代碼層的判斷中,如果S不等於0,那麼就有兩種情況:
1. write 鎖被獲取:S&0x0000FFFF > 0
2. read 鎖被獲取:S>>>16 > 0

=== 點擊查看top目錄 ===

三、lock.readLock().lock() (讀)剖析

  • 看下 ReadLock#lock 方法
public void ReentrantReadWriteLock.ReadLock#lock() {
    sync.acquireShared(1);
}
  • 看下 AbstractQueuedSynchronizer#acquireShared 方法
public final void AbstractQueuedSynchronizer#acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
    	// 返回 -1 才走這裏,進入隊列阻塞等待
        doAcquireShared(arg);
}
  • 看下 ReentrantReadWriteLock.Sync#tryAcquireShared 方法

protected final int ReentrantReadWriteLock.Sync#tryAcquireShared(int unused) {

    Thread current = Thread.currentThread();
    // 當前狀態
    int c = getState();
    
    /*
    	畫個重點:
    	 1. 如果 write 鎖線程數不是0 ,但是如果是當前thread持有鎖的話,同樣可以去獲取讀鎖。這裏成爲鎖降級。
    	 2. 如果 write 鎖線程數不是0, 但是其他 thread 來獲取鎖的話,直接就返回 -1了
     */	 
    // 如果 write 的獨佔鎖線程數不等於0,並且不是當前thread,那麼就返回 -1,表示獲取鎖失敗
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;

    // read 鎖的數量
    int r = sharedCount(c);
    /* 
     * readerShouldBlock :
     * read 鎖是否需要blocking。如是非公平鎖 NonfairLock ,那麼不用排隊直接搶。
     * 如果是公平鎖 FairLock,那麼就判斷下是否線程中有沒有sync隊列,如果沒有,那麼不用排隊,如果有,判斷下是不是重入,是的話,不用排隊。
     */
    if (!readerShouldBlock() &&
    	// 看下是否撐爆炸了,read 鎖的數量要小於最大數 MAX_COUNT
        r < MAX_COUNT &&
        // 設置一下 read 鎖的數量
        compareAndSetState(c, c + SHARED_UNIT)) {

        if (r == 0) { //  1. read 鎖數量是0 
            firstReader = current; 
            firstReaderHoldCount = 1; //第一個 reader 拿到了1個 read 鎖
        } else if (firstReader == current) { // 2. 第一個 reader 重入
            firstReaderHoldCount++; // read 鎖數量 ++
        } else { // 3. read 鎖數量不是0,而且 firstReader 不是當前 thread
            HoldCounter rh = cachedHoldCounter; // 計數器
            // 計數器爲空或者計數器上的 tid不是當前thread的tid
            if (rh == null || rh.tid != getThreadId(current))
            	// 獲取當前thread的計數器
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
            	// 數量是 0的話,set進去
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}

代碼細節分析:

  1. exclusiveCount(c),先判斷 write 鎖是否是0 ,不是的話,判斷是否是當前線程,也不是的話,直接返回 -1
  2. !readerShouldBlock(), 判斷該線程是否需要阻塞,需要的話 ,退出走 fullTryAcquireShared。
  3. r < MAX_COUNT, 判斷下鎖數量是否超出容量了,是的話,退出走 fullTryAcquireShared。
  4. compareAndSetState(c, c + SHARED_UNIT),CAS 設置 read 鎖數量。 不成功的話,退出走 fullTryAcquireShared。
  5. (r == 0) ,上面步驟全部成功,如果 r ==0 也就是 read 鎖數量是 0,那麼設置第一個讀線程firstReader和firstReaderHoldCount
  6. (firstReader == current),如果 r != 0 ,並且 firstReader == current,那麼意味着重入,那麼就直接 ++
  7. else,設置當前thread對應的HoldCounter的值。

=== 點擊查看top目錄 ===

四、lock.writeLock().lock() (寫)剖析

  • 看下 WriteLock#lock 方法
public void ReentrantReadWriteLock.WriteLock#lock() {
    sync.acquire(1);
}
  • 看下 acquire 方法
public final void AbstractQueuedSynchronizer#acquire(int arg) {
    if (!tryAcquire(arg) && // 返回true的話,就是拿到lock了,無需往下走了
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // tryAcquire返回了 false,走這裏進入隊列
        selfInterrupt();
}
  • 看下 tryAcquire 方法

protected final boolean ReentrantReadWriteLock.Sync#tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState(); // 獲取狀態 
    int w = exclusiveCount(c); // 獲取 write 鎖數量
    if (c != 0) {
        // 如果 write 鎖數量不是 0 或者說目前不是重入
        if (w == 0 || current != getExclusiveOwnerThread())
            return false; // 獲取鎖失敗
        // 既然走到了這裏,說明: w!= 0 && current == getExclusiveOwnerThread(),那麼就是write數量不是0,並且是重入操作。
        if (w + exclusiveCount(acquires) > MAX_COUNT) //查看是否超限
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        setState(c + acquires); // 重入,狀態 +1
        return true; //成功拿到鎖
    }
    /*
     * 1. writer 是否應該阻塞。 NonfairSync一直不用阻塞。 FairSync的話,就看下隊列中有沒有排毒跌,有的話,就阻塞
     * 2. CAS 設置 state
     */
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false; // 1.阻塞 或者:2.CAS設置status不成功。返回 false
    setExclusiveOwnerThread(current);
    return true;
}

=== 點擊查看top目錄 ===

五、場景分析

  1. 場景一:thread1 在 read,thread2-threadN 接着 read
  2. 場景二:thread1 在 read,thread1 準備來 write
  3. 場景三:thread1 在 read,thread2 準備來 write
  4. 場景四:thread1 在 read,thread1 重入 read (測試read重入)
  5. 場景五:thread1 在 write,thread2-N 準備來 read
  6. 場景六:thread1 在 write,thread2 準備來 write
  7. 場景七:thread1 在 write ,thread1 準備來 read (測試降級)
  8. 場景八:thread1 在 write, thread再次 write (測試write鎖重入)

場景一:thread1 在 read,thread2-threadN 接着 read

代碼:

public class _14_01_TestReadWriteLock {
    public static void main(String[] args) throws Exception {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " wait to lock ...");
                lock.readLock().lock();
                System.out.println(Thread.currentThread().getName() + " get the lock ... ===");
                System.out.println("點擊任意鍵喚醒線程 ...");
                Scanner sc = new Scanner(System.in);
                sc.nextLine();
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + " ready to unlock ...");
                    lock.readLock().unlock();
                }
            }, "Read_" + i).start();
        }
    }
}

輸出:

Read_0 wait to lock ...
Read_1 wait to lock ...
Read_1 get the lock ... ===
點擊任意鍵喚醒線程 ...
Read_0 get the lock ... ===
點擊任意鍵喚醒線程 ...
Read_2 wait to lock ...
Read_2 get the lock ... ===
點擊任意鍵喚醒線程 ...
Read_3 wait to lock ...
Read_3 get the lock ... ===
點擊任意鍵喚醒線程 ...
Read_4 wait to lock ...
Read_4 get the lock ... ===
點擊任意鍵喚醒線程 ...


Read_3 ready to unlock ...
Read_2 ready to unlock ...
Read_1 ready to unlock ...
Read_0 ready to unlock ...
Read_4 ready to unlock ...

結論:

在第一個thread並未釋放鎖的時候,其他thread不阻塞,直接獲取鎖,往下跑。

=== 點擊查看top目錄 ===

場景二:thread1 在 read,thread1 準備來 write

看下代碼:

public class _14_02_TestReadWriteLock {
	public static void main(String[] args) throws Exception{
		new Thread(() -> {
			ReadWriteLock lock = new ReentrantReadWriteLock();
			System.out.println(Thread.currentThread().getName() + " wait to read lock  ...");
			lock.readLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");


			System.out.println(Thread.currentThread().getName() + " wait to lock write ...");
			lock.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the lock write ...");

			System.out.println(Thread.currentThread().getName() + " ready to unlock read ...");
			lock.readLock().unlock();

			System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
			lock.writeLock().unlock();
		}).start();
	}
}

輸出:

Thread-0 wait to read lock  ...
Thread-0 get the read lock ... ===
Thread-0 wait to lock write ...
... 阻塞當中 ...

上面代碼,read鎖已經獲得,但是write鎖阻塞,read鎖無法釋放,上面死鎖了。

注意上方的 c = getState() = 65536 ,剛剛好是 2的 16次方,因爲read鎖是在 高16位,所以 read 鎖數量是1的話,那麼就是 65536,真正的數量右移 16位。

在這裏插入圖片描述

在這裏插入圖片描述
=== 點擊查看top目錄 ===

場景三:thread1 在 read,thread2 準備來 write

同理,堵塞,等待 thread1 釋放read 鎖,釋放後,可以獲得鎖

// 場景三:thread1 在 read,thread2 準備來 write
public class _14_03_TestReadWriteLock {
	public static void main(String[] args) throws Exception{
		ReadWriteLock lock = new ReentrantReadWriteLock();
		new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + " wait to read lock  ...");
			lock.readLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");

			System.out.println("點擊任意鍵喚醒線程 ...");
			Scanner sc = new Scanner(System.in);
			sc.nextLine();
			System.out.println(Thread.currentThread().getName() + " ready to unlock read ...");
			lock.readLock().unlock();

		}).start();

		Thread.sleep(2000);

		for (int i = 0; i < 5; i++) {
			new Thread(() -> {
				System.out.println(Thread.currentThread().getName() + " wait to lock write ...");
				lock.writeLock().lock();
				System.out.println(Thread.currentThread().getName() + " get the lock write ...");

				System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
				lock.writeLock().unlock();
			}).start();
		}
	}
}

輸出:

Thread-0 wait to read lock  ...
Thread-0 get the read lock ... ===
點擊任意鍵喚醒線程 ...
Thread-1 wait to lock write ...
Thread-2 wait to lock write ...
Thread-3 wait to lock write ...
Thread-4 wait to lock write ...
Thread-5 wait to lock write ...

Thread-0 ready to unlock read ...
Thread-1 get the lock write ...
Thread-1 ready to unlock write ...
Thread-2 get the lock write ...
Thread-2 ready to unlock write ...
Thread-3 get the lock write ...
Thread-3 ready to unlock write ...
Thread-4 get the lock write ...
Thread-4 ready to unlock write ...
Thread-5 get the lock write ...
Thread-5 ready to unlock write ...

=== 點擊查看top目錄 ===

場景四:thread1 在 read,thread1 重入 read (測試read重入)

代碼:

// 場景四:thread1 在 read,thread1 重入 read
public class _14_07_TestReadWriteLock {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            ReadWriteLock lock = new ReentrantReadWriteLock();

            System.out.println(Thread.currentThread().getName() + " wait to read lock ...");
            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");

            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + " get the read lock2 ... ===");

            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + " get the read lock3 ... ===");


            System.out.println(Thread.currentThread().getName() + " ready to read unlock ...");
            lock.readLock().unlock();

            System.out.println(Thread.currentThread().getName() + " ready to read unlock2 ...");
            lock.readLock().unlock();

            System.out.println(Thread.currentThread().getName() + " ready to read unlock3 ...");
            lock.readLock().unlock();
        }).start();
    }
}

輸出:

Thread-0 wait to read lock ...
Thread-0 get the read lock ... ===
Thread-0 get the read lock2 ... ===
Thread-0 get the read lock3 ... ===
Thread-0 ready to read unlock ...
Thread-0 ready to read unlock2 ...
Thread-0 ready to read unlock3 ...

場景五:thread1 在 write,thread2-N 準備來 read

代碼:

public class _14_05_TestReadWriteLock {
	public static void main(String[] args) throws Exception{
		ReadWriteLock lock = new ReentrantReadWriteLock();
		new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + " wait to write lock  ...");
			lock.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");

			System.out.println("點擊任意鍵喚醒線程 ...");
			Scanner sc = new Scanner(System.in);
			sc.nextLine();
			System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
			lock.writeLock().unlock();

		}).start();

		Thread.sleep(5000);

		for (int i = 0; i < 5; i++) {
			new Thread(() -> {
				System.out.println(Thread.currentThread().getName() + " wait to read lock  ... ");
				lock.readLock().lock();
				System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");
				System.out.println(Thread.currentThread().getName() + " ready to read unlock  ...");
				lock.readLock().unlock();
			}).start();
		}
	}
}

輸出:

Thread-0 wait to write lock  ...
Thread-0 get the write lock ... ===
點擊任意鍵喚醒線程 ...
Thread-1 wait to read lock  ... 
Thread-2 wait to read lock  ... 
Thread-3 wait to read lock  ... 
Thread-4 wait to read lock  ... 
Thread-5 wait to read lock  ... 

Thread-0 ready to unlock write ...
Thread-1 get the read lock ... ===
Thread-1 ready to read unlock  ...
Thread-2 get the read lock ... ===
Thread-2 ready to read unlock  ...
Thread-3 get the read lock ... ===
Thread-3 ready to read unlock  ...
Thread-5 get the read lock ... ===
Thread-5 ready to read unlock  ...
Thread-4 get the read lock ... ===
Thread-4 ready to read unlock  ...

read共享鎖解析

在這裏插入圖片描述

=== 點擊查看top目錄 ===

場景六:thread1 在 write,thread2 準備來 write

代碼

public class _14_06_TestReadWriteLock {
	public static void main(String[] args) throws Exception{
		ReadWriteLock lock = new ReentrantReadWriteLock();
		new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + " wait to write lock  ...");
			lock.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");

			System.out.println("點擊任意鍵喚醒線程 ...");
			Scanner sc = new Scanner(System.in);
			sc.nextLine();
			System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
			lock.writeLock().unlock();
		}).start();

		Thread.sleep(5000);

		new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + " wait to write lock  ... ");
			lock.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");
			System.out.println(Thread.currentThread().getName() + " ready to write unlock  ...");
			lock.writeLock().unlock();
		}).start();
	}
}

輸出:

Thread-0 wait to write lock  ...
Thread-0 get the write lock ... ===
點擊任意鍵喚醒線程 ...
Thread-1 wait to write lock  ... 

Thread-0 ready to unlock write ...
Thread-1 get the write lock ... ===
Thread-1 ready to write unlock  ...

場景七:thread1 在 write ,thread1 準備來 read (測試降級)

代碼:

public class _14_07_TestReadWriteLock {
	public static void main(String[] args) throws Exception{
		new Thread(() -> {
			ReadWriteLock lock = new ReentrantReadWriteLock();

			System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
			lock.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");

			System.out.println(Thread.currentThread().getName() + " wait to read lock ...");
			lock.readLock().lock();
			System.out.println(Thread.currentThread().getName() + " get the read lock  ... ===");

			System.out.println(Thread.currentThread().getName() + " ready to unlock read...");
			lock.readLock().unlock();

			System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
			lock.writeLock().unlock();
		}).start();
	}
}

輸出:

Thread-0 wait to write lock ...
Thread-0 get the write lock ... ===
Thread-0 wait to read lock ...
Thread-0 get the read lock  ... ===
Thread-0 ready to unlock read...
Thread-0 ready to unlock write ...

看下圖: 在 write鎖被持有,並且getExclusiveOwnerThread() == current的情況下,順利拿到 read 鎖。

=== 點擊查看top目錄 ===

場景八:thread1 在 write, thread再次 write (測試write鎖重入)

代碼

// 場景八:thread1 在 write, thread再次 write (測試write鎖重入)
public class _14_08_TestReadWriteLock {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            ReadWriteLock lock = new ReentrantReadWriteLock();

            System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");

            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + " get the write lock2 ... ===");

            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + " get the write lock3 ... ===");

            System.out.println(Thread.currentThread().getName() + " ready to write unlock ...");
            lock.writeLock().unlock();

            System.out.println(Thread.currentThread().getName() + " ready to write unlock2 ...");
            lock.writeLock().unlock();

            System.out.println(Thread.currentThread().getName() + " ready to write unlock3 ...");
            lock.writeLock().unlock();
        }).start();
    }
}

輸出:

Thread-0 wait to write lock ...
Thread-0 get the write lock ... ===
Thread-0 get the write lock2 ... ===
Thread-0 get the write lock3 ... ===
Thread-0 ready to write unlock ...
Thread-0 ready to write unlock2 ...
Thread-0 ready to write unlock3 ...

六、各個場景的分析結論:

  1. thread1 拿到read鎖的時候,thread2想read可以。(共享鎖可以共享)
  2. thread1 拿到read鎖的時候,thread1想write不行。(鎖無法升級)
  3. thread1 拿到read鎖的時候,thread2想write不行。
  4. thread1 拿到read鎖的時候,thread1想再次read可以。 (重入ok)
  5. thread1 拿到write鎖的時候,thread2想read不行。(獨佔鎖不允許共享)
  6. thread1 拿到write鎖的時候,thread2 想 write不行。(獨佔鎖不允許共享)
  7. thread1 拿到write鎖的時候,thread1想read可以。(鎖降級了)
  8. thread1 拿到write鎖的時候,thread1想write可以。 (重入ok)

所以:

  1. 一個線程要想同時持有寫鎖和讀鎖,必須先獲取寫鎖再獲取讀鎖;
  2. 寫鎖可以“降級”爲讀鎖;讀鎖不能“升級”爲寫鎖。

=== 點擊查看top目錄 ===

七、write 鎖釋放 tryRelease

protected final boolean ReentrantReadWriteLock.Sync#tryRelease(int releases) {
    //若鎖的持有者不是當前線程,拋出異常
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    //寫鎖的新線程數,正常-1
    int nextc = getState() - releases;
    //如果獨佔模式重入數爲0了,說明獨佔模式write鎖被釋放
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        //若write鎖的新線程數爲0,則將鎖的持有者設置爲null
        setExclusiveOwnerThread(null);
    //設置寫鎖的新線程數
    //不管獨佔模式是否被釋放,更新獨佔重入數
    setState(nextc);
    return free;
}

=== 點擊查看top目錄 ===

八、read 鎖釋放 tryReleaseShared

protected final boolean tryReleaseShared(int unused) {
    // 獲取當前線程
    Thread current = Thread.currentThread();
    if (firstReader == current) { // 當前線程爲第一個讀線程
        // assert firstReaderHoldCount > 0;
        if (firstReaderHoldCount == 1) // read 線程佔用的資源數爲1
            firstReader = null;
        else // 減少佔用的資源
            firstReaderHoldCount--;
    } else { // 當前線程不爲第一個讀線程
        // 獲取緩存的計數器
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current)) // 計數器爲空或者計數器的tid不爲當前正在運行的線程的tid
            // 獲取當前線程對應的計數器
            rh = readHolds.get();
        // 獲取計數
        int count = rh.count;
        if (count <= 1) { // 計數小於等於1
            // 移除
            readHolds.remove();
            if (count <= 0) // 計數小於等於0,拋出異常
                throw unmatchedUnlockException();
        }
        // 減少計數
        --rh.count;
    }
    for (;;) { // 無限循環
        // 獲取狀態
        int c = getState();
        // 獲取狀態
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc)) // 比較並進行設置
            return nextc == 0;
    }
}

=== 點擊查看top目錄 ===

九、結論

而readwrite鎖有以下三個重要的特性:

(1)公平選擇性:支持非公平(默認)和公平的鎖獲取方式,吞吐量還是非公平優於公平。

(2)重進入:讀鎖和寫鎖都支持線程重進入。

(3)鎖降級:遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成爲讀鎖

=== 點擊查看top目錄 ===

十、番外篇

下一章節:【線程】線程八鎖與Synchronzied內部原理(十二)
上一章節:【線程】ReentrantLock 內部公平鎖與非公平鎖實現 (十)

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