欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JUC代码浅析[3]——基于AQS的锁ReentrantReadWriteLock

程序员文章站 2022-06-07 20:49:23
...

JUC 代码浅析 [3] ——基于 AQS 的锁 ReentrantReadWriteLock

       ReentrantReadWriteLock 也是基于 AQS 实现的锁,它的特点是一个资源能够被多个读线程访问,或者被一个写线程访问,读和写是互斥的,可以同时有多个读但只能有一个写,大量读操作的场景下性能较好。 ReentrantReadWriteLock 并没有实现 Lock 接口,而是内部实现了 ReadLock WriteLock 分别针对读和写操作。看起来像两个锁,但其实这两个锁只是进行了一层适配,它们的主体都是 Sync ReadLock WriteLock 分别使用 Sync 的共享和独占模式。

 

ReadLock 加锁和释放,就是向 sync 请求共享模式进入和释放

        public void lock () {

            sync.acquireShared(1);

        }

        public   void unlock () {

            sync.releaseShared(1);

        }

 

WriteLock 加锁和释放,就是向 sync 请求互斥模式进入和释放

public void lock() {

            sync.acquire (1);

        }

public void unlock () {

            sync.release(1);

        }

 

       sync 的实现有公平和非公平两种 ( 下面介绍区别 ) sync 使用 int 的低 16 位表示写计数,高 16 位表示读计数,所以读锁和写锁的最高重入次数为 65535 ,超出将抛出 Error

获取写锁的实现片段,逻辑是:

l  如果读计数不为 0 ,失败;

l  如果写计数不为 0 并且不是当前线程持有锁,失败

l  如果写计数超出 65535 ,失败,抛出 Error

l  如果写计数为 0 并且当前线程需要阻塞,或者 state 被其他线程改变了,失败

l  以上条件都不满足则当前线程拥有写锁

protected final boolean tryAcquire ( int acquires) {

 

            Thread current = Thread.currentThread();

            int c = getState();

            int w = exclusiveCount(c);

            if (c != 0) {

                // (Note: if c != 0 and w == 0 then shared count != 0)

                if (w == 0 || current != getExclusiveOwnerThread())

                    return false ;

                if (w + exclusiveCount(acquires) > MAX_COUNT)

                    throw new Error( "Maximum lock count exceeded" );

            }

            if ((w == 0 && writerShouldBlock(current)) ||

                !compareAndSetState(c, c + acquires))

                return false ;

            setExclusiveOwnerThread(current);

            return true ;

        }

 

       非公平 sync 的写线程在总是不需要阻塞

        final boolean writerShouldBlock (Thread current) {

            return false ; // writers can always barge

        }

 

       公平 sync 的写线程只要 AQS 队列不为空并且不在第一个位置的就需要阻塞

        final boolean writerShouldBlock(Thread current) {

            // only proceed if queue is empty or current thread at head

            return !isFirst(current);

        }

 

 

       写锁的释放,减去并检查 state 计数,为 0 则表示写锁已经可以释放了,不为 0 说明该线程重入了写锁并把剩余的计数写入 state

        protected final boolean tryRelease ( int releases) {

            int nextc = getState() - releases;

            if (Thread.currentThread() != getExclusiveOwnerThread())

                throw new IllegalMonitorStateException();

            if (exclusiveCount(nextc) == 0) {

                setExclusiveOwnerThread( null );

                setState(nextc);

                return true ;

            } else {

                setState(nextc);

                return false ;

            }

        }

 

读锁的获取,

1 如果已经有线程获得写锁,并且不是当前线程获得的,失败

2 如果重入的读计数超出 65535 ,失败,抛出 Error

3 否则如果线程不用阻塞,并且增加读计数成功,则成功获得读锁

4 如果第三步失败(可能由于线程需要阻塞或者 CAS 修改失败),   不断尝试去修改状态直到成功或者锁被写入线程占有(看 fullTryAcquireShared 的实现 )

        protected final int tryAcquireShared ( int unused) {

            Thread current = Thread.currentThread();

            int c = getState();

            if (exclusiveCount(c) != 0 &&

                getExclusiveOwnerThread() != current)

                return -1;

            if (sharedCount(c) == MAX_COUNT)

                throw new Error( "Maximum lock count exceeded" );

            if (!readerShouldBlock(current) &&

                compareAndSetState(c, c + SHARED_UNIT)) {

                HoldCounter rh = cachedHoldCounter;

                 if (rh == null || rh.tid != current.getId())

                    cachedHoldCounter = rh = readHolds.get();

                rh.count++;

                return 1;

            }

            return fullTryAcquireShared(current);

    }

 

final int fullTryAcquireShared(Thread current) {

            HoldCounter rh = cachedHoldCounter;

            if (rh == null || rh.tid != current.getId())

                rh = readHolds.get();

            for (;;) {

                int c = getState();

                int w = exclusiveCount(c);

                if ((w != 0 && getExclusiveOwnerThread() != current) ||

                    ((rh.count | w) == 0 && readerShouldBlock(current)))

                    return -1;

                if (sharedCount(c) == MAX_COUNT)

                    throw new Error( "Maximum lock count exceeded"