ReentrantReadWriteLock读写锁源码解析

1.基本概念

读写锁是基于AQS构建,它包含有读锁和写锁,含有公平和非公平机制。Sync继承AbstractQueuedSynchronizer,它是构建锁的核心。
业务场景就是对一个共享资源的读频率大于写。

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
        
    private static final long serialVersionUID = -6992448646407690164L;
    /** 读锁 */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** 写锁 */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** 基于AQS的同步机制 */
    final Sync sync;

ReentrantReadWriteLock具有以下特性:

  • 非公平模式(默认方式)和公平模式,公平模式就是线程按照队列的顺序获取锁,非公平模式具有更高的吞吐量。
  • 可重入性,一个锁可以重复获取。
  • 锁降级,可以从写锁降为读锁,方法是获取写锁,然后是读锁,然后释放写锁。 但是,无法从读锁升级到写锁。
  • 锁获取中断,在获取读锁和写锁时支持中断。

2. 源码解析

2.1 构造一个ReentrantReadWriteLock。

 	/**
     * 默认是非公平模式
     */
    public ReentrantReadWriteLock() {
        this(fair);
    }
 	/**
     * fair决定是公平模式还是非公平模式
     */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

NonfairSync和FairSync都继承Sync(Sync继承AQS)。都有writerShouldBlock和readerShouldBlock,判断当前线程是否应该阻塞,在非公平和公平模式下得判断集中不同,源码如下:

 /**
     * Nonfair version of Sync
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        final boolean writerShouldBlock() {
            return false; // 非公平模式下写锁不会阻塞,即无视队列,不需排队
        }
        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();
        }
    }

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

2.2 读锁源码解析,基于AQS共享模式acquireShared,多线程通过tryAcquireShared方法获取共享锁,返回小于0获取失败,反之获取成功。

 		/**
        *如果写锁没有被另一个线程持有,则获取读锁并立即返回。
		*如果写锁被另一个线程持有,那么当前线程将被禁用以用于线程调度目的	  并处于休眠状态,直到获得读锁为止。
        */
        public void lock() {
            sync.acquireShared(1);
        }

		public final void acquireShared(int arg) {
		/**
        *尝试获取锁。
        */
        if (tryAcquireShared(arg) < 0)
	        /**
	        *尝试锁失败时,放入队列。
	        */
            doAcquireShared(arg);
    	}





         protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             */
            Thread current = Thread.currentThread();
            int c = getState();
             /*
             * 如果写锁重入次数不等于0,则表示写锁被某个线程占有
             * 并且获取独占锁的线程不是当前线程,则返回-1
             */
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
             /*
             * 获取读锁计数
             */
            int r = sharedCount(c);
            /*
             * 获取写锁持有线程数
             * readerShouldBlock调用apparentlyFirstQueuedIsExclusive:如果请求读锁的当前线程发现同步队列的 head 节点的下一个节点为排他式节点,那么就说明有一个线程在等待获取写锁(争抢写锁失败,被放入到同步队列中),那么请求读锁的线程就要阻塞
             */
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    /*
             		* 第一个获取读锁,记录为firstReader 
             		*/
                    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);
                        //计数器加1
                    rh.count++;
                }
                return 1;
            }
            /*
            * 获取失败自旋重试
            */
            return fullTryAcquireShared(current);
        }

2.3 通过releaseShared释放锁。


 public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

/**
* 尝试释放读锁
*/
protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            //当前线程为第一个读线程
            if (firstReader == current) {
                //第一个读线程锁计数1,则直接赋值为null
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                //第一个读线程锁计数减1
                    firstReaderHoldCount--;
            } else {
            	//从缓存计数器获取
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                //计数器减1
                --rh.count;
            }
            // 自旋 CAS比较设置
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值