一起读源码(二) ReadWriteLock源码ReadLock解析

Java并发包中ReadWriteLock是一个接口,主要有两个方法

public interface ReadWriteLock {
    /**
     * 返回读锁
     */
    Lock readLock();

    /**
     * 返回写锁
     */
    Lock writeLock();
}

Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口,并添加了可重入的特性

构造方法

ReentrantReadWriteLock有两个构造方法

    public ReentrantReadWriteLock() {
        this(false);
    }

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

默认的构造方法使用的是非公平模式,创建的Sync是NonfairSync对象,然后初始化读锁和写锁。

同步器

FairSync的实现:

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

再来看NonfairSync的实现:

static final class NonfairSync extends Sync {
       private static final long serialVersionUID = -8159625535654395037L;
       final boolean writerShouldBlock() {
           return false; // writers can always barge
       }
       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();
       }
   }

非公平模式下,writerShouldBlock直接返回false,说明不需要阻塞;而readShouldBlock调用了apparentFirstQueuedIsExcluisve()方法。
继承AQS的类都需要使用state变量代表某种资源,ReentrantReadWriteLock中的state代表了读锁的数量和写锁的持有与否,整个结构如下:
一起读源码(二) ReadWriteLock源码ReadLock解析_第1张图片
state的高16位代表读锁的个数,低16位代表写锁的状态

获取锁

读锁的获取
当需要使用读锁时,首先调用lock方法

	public void lock() {
        sync.acquireShared(1);
    }

AQS的acquireShared方法

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

当tryAcquireShared()方法小于0时,那么会执行doAcquireShared方法将该线程加入到等待队列中。

	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, which is postponed to full version
	     *    to avoid having to check hold count in
	     *    the more typical non-reentrant case.
	     * 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();
	    //如果当前有写线程并且本线程不是写线程,不符合重入,失败
	    if (exclusiveCount(c) != 0 &&
	        getExclusiveOwnerThread() != current)
	        return -1;
	    //得到读锁的个数
	    int r = sharedCount(c);
	    //如果读不应该阻塞并且读锁的个数小于最大值65535,并且可以成功更新状态值,成功
	    if (!readerShouldBlock() &&
	        r < MAX_COUNT &&
	        compareAndSetState(c, c + SHARED_UNIT)) {
	        //如果当前读锁为0
	        if (r == 0) {
	            //第一个读线程就是当前线程
	            firstReader = current;
	            firstReaderHoldCount = 1;
	        }
	        //如果当前线程重入了,记录firstReaderHoldCount
	        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);
	            rh.count++;
	        }
	        return 1;
	    }
	    //循环尝试
	    return fullTryAcquireShared(current);
	}

fullTryAcquiredShared方法

final int fullTryAcquireShared(Thread current) {
        HoldCounter rh = null;
        for (;;) {
            int c = getState();
            //一旦有别的线程获得了写锁,返回-1,失败
            if (exclusiveCount(c) != 0) {
                if (getExclusiveOwnerThread() != current)
                    return -1;
            } 
            //如果读线程需要阻塞
            else if (readerShouldBlock()) {
                // Make sure we're not acquiring read lock reentrantly
                if (firstReader == current) {
                    // assert firstReaderHoldCount > 0;
                }
                //说明有别的读线程占有了锁
                else {
                    if (rh == null) {
                        rh = cachedHoldCounter;
                        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)) {
                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;
            }
        }
    }

你可能感兴趣的:(JDK源码解析,并发)