读写锁简要源码分析

声明对象方式

//构造方法传boolean以声明公平or非公平锁(非公平吞吐量大于公平)
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
writeLock.lock();
readLock.lock();

源码简要分析

/*
 * 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;
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

/** Returns the number of shared holds represented in count  */
//右移16位,显然返回高16位,即读锁(共享)的被持有数
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count  */
//即state & (1,00000000,00000000-1)  显然低位全部为1,高16位全为0,所以可以计算出是否写锁已被持有
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
读锁获取
    /**
	 * Acquires the read lock.
	 *  
	 * 如果写锁没有被别的线程持有,将立刻获取读锁且立刻返回
	 * <p>Acquires the read lock if the write lock is not held by
	 * another thread and returns immediately.
	 *
	 如果写锁被背的线程持有,则当前线程将无法被调起处于休眠状态直到读锁被获取
	 lies dormant :处于休眠
	 * <p>If the write lock is held by another thread then
	 * the current thread becomes disabled for thread scheduling
	 * purposes and lies dormant until the read lock has been acquired.
	 */
	 public void lock() {
	    sync.acquireShared(1);
	 }

    /**
	  * Acquires in shared mode, ignoring interrupts.  Implemented by
	  * first invoking at least once {@link #tryAcquireShared},
	  * returning on success.  Otherwise the thread is queued, possibly
	  * repeatedly blocking and unblocking, invoking {@link
	  * #tryAcquireShared} until success.
	  *
	  *以共享模式获取,忽略中断。通过首先调用至少一次tryAcquireShared来实现,成功返回。否则线程排队,
      *可能重复阻塞和解除阻塞,调用tryAcquireShared直到成功 
	  */
	 public final void acquireShared(int arg) {
	     if (tryAcquireShared(arg) < 0)
	         doAcquireShared(arg);
	 }

     protected final int tryAcquireShared(int unused) {
         /*
          * Walkthrough:
          * 1. If write lock held by another thread, fail.
          * 2. Otherwise, this thread is eligible for
          *    lock wrt state, so ask if it should block
          *    because of queue policy. If not, try
          *    to grant by CASing state and updating count.
          *    Note that step does not check for reentrant
          *    acquires
          * 3. If step 2 fails either because thread
          *    apparently not eligible or CAS fails or count
          *    saturated, chain to version with full retry loop.
          */
         Thread current = Thread.currentThread();
         int c = getState();
         //如果当前写锁已被持有,且持有写锁线程不为该线程,则直接返回-1
         if (exclusiveCount(c) != 0 &&
             getExclusiveOwnerThread() != current)
             return -1;
         //获取读锁的被持有数
         int r = sharedCount(c);
         //如果当前读锁获取不需要阻塞,且被持有数量小于最大可持有数,且state可正常CAS计算,则返回1
         if (!readerShouldBlock() &&
             r < MAX_COUNT &&
             compareAndSetState(c, c + SHARED_UNIT)) {
             if (r == 0) {
                 firstReader = current;
                 firstReaderHoldCount = 1;
             } else if (firstReader == current) {
                 firstReaderHoldCount++;
             } else {
             	 // 除第一个线程之后的其他线程获得读锁
            	 // 每个线程每次获得读锁重入计数+1
            	 // readHolds 就是一个ThreadLocal,里面放的HoldCounter,用来统计每个线程的重入次数 
                 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 1;
         }
		 //
         return fullTryAcquireShared(current);
     }

	//AQS-查看获取读锁是否应该阻塞
	final boolean readerShouldBlock() {
    	return hasQueuedPredecessors();
    }
	//true:需要阻塞  false:不需阻塞
	public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        //若头结点不等于尾结点则表示存在节点于头尾之间,即正在排队的节点线程
        //如果h.next为空(表示已有线程先于该线程排队)则需要排队
        //若下一个节点仍不是当前线程,则需要排队;反之不需要排队
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

	//读锁添加失败就进入到这里,进入阻塞队列,同时再次尝试获取读锁
	private void doAcquireShared(int arg) {
 
	    //addWaiter参数为节点为共享模式flag,含义为:Marker to indicate a node is waiting in shared mode
	    // addWaiter()方法中如果是首次进会调用enq(node)来完成初始化,否则就将共享节点添加到队列的尾部,并且返回这个共享节点
	    final Node node = addWaiter(Node.SHARED);
	    boolean failed = true;
	    try {
	        boolean interrupted = false;
	        
	        // 开始自旋
	        for (;;) {
	            // 获取到当前共享节点的前一个节点
	            final Node p = node.predecessor();
	 
	            // 如果当前共享节点的前一个节点是head节点,也就是sentinel哨兵节点,就会尝试获取到读锁
	            if (p == head) {
	            
	                // 再次尝试获取到读锁 成功是1,不成功是-1,也影响着下面的判断。
	                int r = tryAcquireShared(arg);
	 
	                // 如果再次尝试获取读锁成功就执行下面代码,失败就往下走。
	                if (r >= 0) {
	 
	                    // 这里是尝试获取成功,则重新设置HEAD(将head节点指向当前节点,意思就是当前节点当sentinel哨兵节点了)
	                    // 并且对于共享节点还有一个任务就是唤醒后面的共享节点,所以当前节点后面如果是读锁也会被唤醒,所以也就是读读并发。
	                    setHeadAndPropagate(node, r);
	 
	                    // 将之前的head节点,也就是sentinel哨兵节点的next指向null,所以此节点没有任何引用了,就被回gc回收
	                    p.next = null; // help GC
	                    
	                    // 判断是否是被打断醒来的。
	                    if (interrupted)
	                        selfInterrupt();
	                    failed = false;
	                    return;
	                }
	            }
	 
	            // 到这里就代表前面的尝试获取读锁失败了
	            // shouldParkAfterFailedAcquire()方法将当前节点的前一个节点的waitState标志位设为-1
	            // 但是设置的过程还是自旋一次尝试获取锁
	            // parkAndCheckInterrupt()方法是将当前线程park休眠。
	            // 当前线程park之前,总共是经历过3次尝试获取读锁。
	            // 注意等unLock()方法执行了就会从这里唤醒线程。所以还是在自旋中。醒来又会去尝试获取锁
	            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
	                interrupted = true;
	        }
	    } finally {
	        if (failed)
	            cancelAcquire(node);
	    }
读锁释放
	protected final boolean tryReleaseShared(int unused) {
    	Thread current = Thread.currentThread();
    	//如果last firstReader = current,则递减计数器(若仅重入一次,则回收对象以释放内存)
        if (firstReader == current) {
            // assert firstReaderHoldCount > 0;
            if (firstReaderHoldCount == 1)
                firstReader = null;
            else
                firstReaderHoldCount--;
        } else {
        	//获取cachedHoldCounter(为上一次获取读锁的线程对象,因为往往获取锁后,紧接着就是释放,这样做缓存可以避免查找)
            HoldCounter rh = cachedHoldCounter;
            //获取rh正确引用以得到hold count
            if (rh == null || rh.tid != getThreadId(current))
                rh = readHolds.get();
            int count = rh.count;
            if (count <= 1) {
                readHolds.remove();
                if (count <= 0)
                    throw unmatchedUnlockException();
            }
            --rh.count;
        }
        for (;;) {
            int c = getState();
            //上文已经提到过,读锁为高16位,故count--操作不简单的视为-1,必须转换为1<<16以达到减一操作
            int nextc = c - SHARED_UNIT;
            if (compareAndSetState(c, nextc))
                // Releasing the read lock has no effect on readers,
                // but it may allow waiting writers to proceed if
                // both read and write locks are now free.
                return nextc == 0;
        }
     }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值