java并发锁ReentrantReadWriteLock读写锁源码分析

来源:互联网 发布:图像匹配算法 编辑:程序博客网 时间:2024/05/06 12:28
1、ReentrantReadWriterLock基础
所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为 如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。
java.util.concurrent.locks中关于多写锁的接口:ReadWriteLock
public interface ReadWriteLock {    /**     * Returns the lock used for reading.     *     * @return the lock used for reading.     */    Lock readLock();    /**     * Returns the lock used for writing.     *     * @return the lock used for writing.     */    Lock writeLock();}


提一个问题,是否觉得ReentrantReadWriteLock会实现Lock接口吗?与ReentrantLock有什么关系?
答案是否定的,ReentrantReadWriterLock通过两个内部类实现Lock接口,分别是ReadLock,WriterLock类。与ReentrantLock一样,ReentrantReadWriterLock同样使用自己的内部类Sync(继承AbstractQueuedSynchronizer)实现CLH算法。为了方便对读写锁获取机制的了解,先介绍一下Sync内部类中几个属性,采用了位运算:
/*         * 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  */        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }        /** Returns the number of exclusive holds represented in count  */        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
首先ReentrantReadWriterLock使用一个32位的int类型来表示锁被占用的线程数(ReentrantLock中的state),用所以,采取的办法是,高16位用来表示读锁占有的线程数量,用低16位表示写锁被同一个线程申请的次数。
        SHARED_SHIFT,表示读锁占用的位数,常量16
        SHARED_UNIT,   增加一个读锁,按照上述设计,就相当于增加 SHARED_UNIT;
        MAX_COUNT    ,表示申请读锁最大的线程数量,为65535
        EXCLUSIVE_MASK  :表示计算写锁的具体值时,该值为 15个1,用 getState & EXCLUSIVE_MASK算出写锁的
                    线程数,大于1表示重入。

          static int sharedCount(int c)    { return c >>> SHARED_SHIFT; } 
          static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
举例说明,比如,现在当前,申请读锁的线程数为13个,写锁一个,那state怎么表示?
上文说过,用一个32位的int类型的高16位表示读锁线程数,13的二进制为 1101,那state的二进制表示为
00000000 00001101 00000000 00000001,十进制数为851969, 接下在具体获取锁时,需要根据这个851968这个值得出上文中的 13 与 1。要算成13,只需要将state 无符号向左移位16位置,得出00000000 00001101,就出13,根据851969要算成低16位置,只需要用该00000000 00001101 00000000 00000001 & 111111111111111(15位),就可以得出00000001,就是利用了1&1得1,1&0得0这个技巧。
移位元素,如果一个数值向左移(<)一位,在没越界(超过该类型表示的最大值)的情况下,想当于操作数 * 2
如果一个数值向右(>) 移动移位,在没有越界的情况下,想到于操作数 除以2。
        然后再关注如下几个与线程本地变量相关的属性:
        
/**         * The number of reentrant read locks held by current thread.         * Initialized only in constructor and readObject.         * Removed whenever a thread's read hold count drops to 0.         */        private transient ThreadLocalHoldCounter readHolds;        /**         * The hold count of the last thread to successfully acquire         * readLock. This saves ThreadLocal lookup in the common case         * where the next thread to release is the last one to         * acquire. This is non-volatile since it is just used         * as a heuristic, and would be great for threads to cache.         *         * <p>Can outlive the Thread for which it is caching the read         * hold count, but avoids garbage retention by not retaining a         * reference to the Thread.         *         * <p>Accessed via a benign data race; relies on the memory         * model's final field and out-of-thin-air guarantees.         */        private transient HoldCounter cachedHoldCounter;        /**         * firstReader is the first thread to have acquired the read lock.         * firstReaderHoldCount is firstReader's hold count.         *         * <p>More precisely, firstReader is the unique thread that last         * changed the shared count from 0 to 1, and has not released the         * read lock since then; null if there is no such thread.         *         * <p>Cannot cause garbage retention unless the thread terminated         * without relinquishing its read locks, since tryReleaseShared         * sets it to null.         *         * <p>Accessed via a benign data race; relies on the memory         * model's out-of-thin-air guarantees for references.         *         * <p>This allows tracking of read holds for uncontended read         * locks to be very cheap.         */        private transient Thread firstReader = null;        private transient int firstReaderHoldCount;

   
        上述这4个变量,其实就是完成一件事情,将获取读锁的线程放入线程本地变量(ThreadLocal),方便从整个上 下文,根据当前线程获取持有锁的次数信息。其实 firstReader,firstReaderHoldCount ,cachedHoldCounter 这三个变量就是为readHolds变量服务的,是一个优化手段,尽量减少直接使用readHolds.get方法的次数,firstReader与firstReadHoldCount保存第一个获取读锁的线程,也就是readHolds中并不会保存第一个获取读锁的线程;cachedHoldCounter 缓存的是最后一个获取线程的HolderCount信息,该变量主要是在如果当前线程多次获取读锁时,减少从readHolds中获取HoldCounter的次数。请结合如下代码理解上述观点:
                if (r == 0) {                    firstReader = current;                    firstReaderHoldCount = 1;                } else if (firstReader == current) {                    firstReaderHoldCount++;                } else {                    HoldCounter rh = cachedHoldCounter;                    if (rh == null || rh.tid != current.getId())                        cachedHoldCounter = rh = readHolds.get();                    else if (rh.count == 0)                        readHolds.set(rh);                    rh.count++;                }
2、ReentrantReadWriterLock源码分析
2.1 ReadLock 源码分析
2.1.1 lock方法
/**         * Acquires the read lock.         *         * <p>Acquires the read lock if the write lock is not held by         * another thread and returns immediately.         *         * <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);        }
sync.acquireShared方法存在于AbstractQueuedSynchronizer类中,
/**     * 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.     *     * @param arg the acquire argument.  This value is conveyed to     *        {@link #tryAcquireShared} but is otherwise uninterpreted     *        and can represent anything you like.     */    public final void acquireShared(int arg) {        if (tryAcquireShared(arg) < 0)    //@1            doAcquireShared(arg);           //@2    }
根据常识,具体获取锁的过程在子类中实现,果不其然,tryAcquireShared方法在ReentrantReadWriterLock的Sync类中实现
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();    //@1 start            int c = getState();            if (exclusiveCount(c) != 0 &&                getExclusiveOwnerThread() != current)                return -1;                                                     // @1 end            int r = sharedCount(c);            if (!readerShouldBlock() &&                                          r < MAX_COUNT &&                compareAndSetState(c, c + SHARED_UNIT)) {    // @2                if (r == 0) {                                      //@21                                                   firstReader = current;                    firstReaderHoldCount = 1;                } else if (firstReader == current) {  //@22                    firstReaderHoldCount++;                } else {                                            // @23                    HoldCounter rh = cachedHoldCounter;                    if (rh == null || rh.tid != current.getId())                        cachedHoldCounter = rh = readHolds.get();                    else if (rh.count == 0)                        readHolds.set(rh);                    rh.count++;                }                return 1;            }            return fullTryAcquireShared(current);      // @3        }
尝试获取共享锁代码解读:
@1 start--end ,如果有线程已经抢占了写锁,并且不是当前线程,则直接返回-1,通过排队获取锁。
@2,如果线程不需要阻塞,并且获取读锁的线程数没有超过最大值,并且使用 CAS更新共享锁线程数量成功的话;表示成获取读锁,然后进行内部变量的相关更新操作;先关注一下,成功获取读锁后,内部变量的更新操作:
@21,如果r=0, 表示,当前线程为第一个获取读锁的线程
@22,如果第一个获取读锁的对象为当前对象,将firstReaderHoldCount 加一
@23,成功获取锁后,如果不是第一个获取多锁的线程,将该线程持有锁的次数信息,放入线程本地变量中,方便在整个请求上下文(请求锁、释放锁等过程中)使用持有锁次数信息。
@3 在讲解代码@3之前,我们先重点分析@2处的第一个条件,是否需要阻塞方法:readerShouldBlock,在具体的子类中,现在查看的是NonfairSync中的方法:
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();   //该方法,具体又是在 AbstractQueuedSynchronizer中        }/**     * Returns {@code true} if the apparent first queued thread, if one     * exists, is waiting in exclusive mode.  If this method returns     * {@code true}, and the current thread is attempting to acquire in     * shared mode (that is, this method is invoked from {@link     * #tryAcquireShared}) then it is guaranteed that the current thread     * is not the first queued thread.  Used only as a heuristic in     * ReentrantReadWriteLock.     */    final boolean apparentlyFirstQueuedIsExclusive() {        Node h, s;        return (h = head) != null &&            (s = h.next)  != null &&            !s.isShared()         &&            s.thread != null;    }
    该方法如果头节点不为空,并头节点的下一个节点不为空,并且不是共享模式【独占模式,写锁】、并且线程不为空。则返回true,说明有当前申请读锁的线程占有写锁,并有其他写锁在申请。为什么要判断head节点的下一个节点不为空,或是thread不为空呢?因为第一个节点head节点是当前持有写锁的线程,也就是当前申请读锁的线程,这里,也就是锁降级的关键所在,如果占有的写锁不是当前线程,那线程申请读锁会直接失败。

现在继续回到@3,讲解如果第一次尝试获取读锁失败后,该如何处理。首先,进入该方法的条件如下:
1),没有写锁被占用时,尝试通过一次CAS去获取锁时,更新失败(说明有其他读锁在申请)。
2),当前线程占有写锁,并且没有有其他写锁在当前线程的下一个节点等待获取写锁。;其实如果是这种情况,除非当前线程占有锁的下个线程取消,否则进入fullTryAcquireShared方法也无法获取锁。
/**         * Full version of acquire for reads, that handles CAS misses         * and reentrant reads not dealt with in tryAcquireShared.         */        final int fullTryAcquireShared(Thread current) {            /*             * This code is in part redundant with that in             * tryAcquireShared but is simpler overall by not             * complicating tryAcquireShared with interactions between             * retries and lazily reading hold counts.             */            HoldCounter rh = null;            for (;;) {                int c = getState();                if (exclusiveCount(c) != 0) {                                     //@31                    if (getExclusiveOwnerThread() != current)                        return -1;                    // else we hold the exclusive lock; blocking here                    // would cause deadlock.                } else if (readerShouldBlock()) {                             //@32                    // Make sure we're not acquiring read lock reentrantly                    if (firstReader == current) {                              //@33                        // assert firstReaderHoldCount > 0;                    } else {                                                              //@34                        if (rh == null) {                            rh = cachedHoldCounter;                            if (rh == null || rh.tid != current.getId()) {                                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)) {     // @35                    if (sharedCount(c) == 0) {                        firstReader = current;                        firstReaderHoldCount = 1;                    } else if (firstReader == current) {                        firstReaderHoldCount++;                    } else {                        if (rh == null)                            rh = cachedHoldCounter;                        if (rh == null || rh.tid != current.getId())                            rh = readHolds.get();                        else if (rh.count == 0)                            readHolds.set(rh);                        rh.count++;                        cachedHoldCounter = rh; // cache for release                    }                    return 1;                }            }        }
代码@31,首先再次判断,如果当前线程不是写锁的持有者,直接返回-1,结束尝试获取读锁,需要排队去申请读锁。
代码@32,如果需要阻塞,说明除了当前线程持有写锁外,还有其他线程已经排队在申请写锁,故,即使申请读锁的线程已经持有写锁(写锁内部再次申请读锁,俗称锁降级)还是会失败,因为有其他线程也在申请写锁,此时,只能结束本次申请读锁的请求,转而去排队,否则,将造成死锁。代码@34,就是从readHolds中移除当前线程的持有数,然后返回-1,结束尝试获取锁步骤(结束tryAcquireShared 方法)然后去排队获取。
代码@33,因为,如果当前线程是第一个获取了写锁,那其他线程无法申请写锁(该部分在分析完,读写锁的队列机制后,才回来做更详细的解答。)
代码@35,表示成功获取读锁,后续就是更新readHolds等内部变量,该部分在上文中已有讲解。如果是通过@35尝试获取锁成功,这就是写锁内部--》再次申请读锁(锁降级)的原理。
至此,完成尝试获取锁步骤 tryAcquireShared 方法,我们再次回到 acquireShared,如果返回-1,那么需要排队申请,具体请看 doAcquireShared(arg);
   public final void acquireShared(int arg) {        if (tryAcquireShared(arg) < 0)    //@1            doAcquireShared(arg);           //@2    }/**     * Acquires in shared uninterruptible mode.     * @param arg the acquire argument     */    private void doAcquireShared(int arg) {        final Node node = addWaiter(Node.SHARED);   //@1        boolean failed = true;        try {            boolean interrupted = false;            for (;;) { // @2,开始自旋重试                final Node p = node.predecessor();   // @3                if (p == head) {                                   // @4                    int r = tryAcquireShared(arg);                             if (r >= 0) {                        setHeadAndPropagate(node, r);    //@5                        p.next = null; // help GC                        if (interrupted)                            selfInterrupt();                        failed = false;                        return;                    }                }                if (shouldParkAfterFailedAcquire(p, node) &&                    parkAndCheckInterrupt())                                              // @6                    interrupted = true;            }        } finally {            if (failed)                cancelAcquire(node);        }    }
获取共享锁解读:
代码@1,在队列尾部增加一个节点。锁模式为共享模式。
代码@3,获取该节点的前置节点。
代码@4,如果该节点的前置节点为head(头部),为什么前置节点是head时,可以再次尝试呢?在讲解ReentrantLock时,也讲过,head节点的初始化在第一次产生锁争用时初始化,刚开始初始化的head节点是不代表线程的,故可以尝试获取锁。如果获取失败,则将进入到shouldParkAfterFailedAcquire和parkAndCheckInterrupt方法中,线程阻塞,等待被唤醒。
重点分析一下获取锁后的操作:setHeadAndPropagate
/**     * Sets head of queue, and checks if successor may be waiting     * in shared mode, if so propagating if either propagate > 0 or     * PROPAGATE status was set.     *     * @param node the node     * @param propagate the return value from a tryAcquireShared     */    private void setHeadAndPropagate(Node node, int propagate) {        Node h = head; // Record old head for check below         setHead(node);        /*         * Try to signal next queued node if:         *   Propagation was indicated by caller,         *     or was recorded (as h.waitStatus) by a previous operation         *     (note: this uses sign-check of waitStatus because         *      PROPAGATE status may transition to SIGNAL.)         * and         *   The next node is waiting in shared mode,         *     or we don't know, because it appears null         *         * The conservatism in both of these checks may cause         * unnecessary wake-ups, but only when there are multiple         * racing acquires/releases, so most need signals now or soon         * anyway.         */        if (propagate > 0 || h == null || h.waitStatus < 0) {   // @1            Node s = node.next;            if (s == null || s.isShared())    // @2                doReleaseShared();          //@3        }    }/**     * Sets head of queue to be node, thus dequeuing. Called only by     * acquire methods.  Also nulls out unused fields for sake of GC     * and to suppress unnecessary signals and traversals.     *     * @param node the node     */    private void setHead(Node node) {        head = node;        node.thread = null;        node.prev = null;    }/**     * Release action for shared mode -- signal successor and ensure     * propagation. (Note: For exclusive mode, release just amounts     * to calling unparkSuccessor of head if it needs signal.)     */    private void doReleaseShared() {        /*         * Ensure that a release propagates, even if there are other         * in-progress acquires/releases.  This proceeds in the usual         * way of trying to unparkSuccessor of head if it needs         * signal. But if it does not, status is set to PROPAGATE to         * ensure that upon release, propagation continues.         * Additionally, we must loop in case a new node is added         * while we are doing this. Also, unlike other uses of         * unparkSuccessor, we need to know if CAS to reset status         * fails, if so rechecking.         */        for (;;) {            Node h = head;            if (h != null && h != tail) {   //@4                int ws = h.waitStatus;                if (ws == Node.SIGNAL) {   //@5                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))                        continue;            // loop to recheck cases                    unparkSuccessor(h);                }                else if (ws == 0 &&                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))   //@6                    continue;                // loop on failed CAS            }            if (h == head)                   // loop if head changed      //@7                break;        }    }
释放共享锁的步骤:
代码@1,如果读锁(共享锁)获取成功,或头部节点为空,或头节点取消,或刚获取读锁的线程的下一个节点为空,或在节点的下个节点也在申请读锁,则在CLH队列中传播下去唤醒线程,怎么理解这个传播呢,就是只要获取成功到读锁,那就要传播到下一个节点(如果一下个节点继续是读锁的申请,只要成功获取,就再下一个节点,直到队列尾部或为写锁的申请,停止传播)。具体请看doReleaseShared方法。
代码@4,从队列的头部开始遍历每一个节点。
代码@5,如果节点状态为 Node.SIGNAL,将状态设置为0,设置成功,唤醒线程。为什么会设置不成功,可能改节点被取消;还有一种情况就是有多个线程在运行该代码段,这就是PROPAGATE的含义吧,传播,请看代码@7的理解。
代码@6,如果状态为0,则设置为Node.PROPAGATE,设置为传播,该值然后会在什么时候变化呢?在判断该节点的下一个节点是否需要阻塞时,会判断,如果状态不是Node.SIGNAL或取消状态,为了保险起见,会将前置节点状态设置为Node.SIGNAL,然后再次判断,是否需要阻塞。
代码@7,如果处理过一次 unparkSuccessor 方法后,头节点没有发生变化,就退出该方法,那head在什么时候会改变呢?当然在是抢占锁成功的时候,head节点代表获取锁的节点。一旦获取锁成功,则又会进入setHeadAndPropagate方法,当然又会触发doReleaseShared方法,传播特性应该就是表现在这里吧。再想一下,同一时间,可以有多个多线程占有锁,那在锁释放时,写锁的释放比较简单,就是从头部节点下的第一个非取消节点,唤醒线程即可,为了在释放读锁的上下文环境中获取代表读锁的线程,将信息存入在 readHolds ThreadLocal变量中。
到这里为止,读锁的申请就讲解完毕了,先给出如下流程图:


                                                                        尝试获取读锁过程


从队列中获取读锁的流程如下:


2.1.2 ReadLock 的 unlock方法详解
public  void unlock() {        sync.releaseShared(1);}//AbstractQueuedSynchronizer的  realseShared方法public final boolean releaseShared(int arg) {        if (tryReleaseShared(arg)) {            doReleaseShared();            return true;        }        return false;    }// ReentrantReadWriterLock.Sync tryReleaseSharedprotected final boolean tryReleaseShared(int unused) {             Thread current = Thread.currentThread();            if (firstReader == current) {                               // @1 start                               // assert firstReaderHoldCount > 0;                if (firstReaderHoldCount == 1)                    firstReader = null;                else                    firstReaderHoldCount--;            } else {                HoldCounter rh = cachedHoldCounter;                if (rh == null || rh.tid != current.getId())                    rh = readHolds.get();                int count = rh.count;                if (count <= 1) {                    readHolds.remove();                    if (count <= 0)                        throw unmatchedUnlockException();                }                --rh.count;                                                            // @1 end            }            for (;;) {                                                               // @2                int c = getState();                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;            }        }AbstractQueuedSynchronizer的doReleaseShared/**     * Release action for shared mode -- signal successor and ensure     * propagation. (Note: For exclusive mode, release just amounts     * to calling unparkSuccessor of head if it needs signal.)     */    private void doReleaseShared() {        /*         * Ensure that a release propagates, even if there are other         * in-progress acquires/releases.  This proceeds in the usual         * way of trying to unparkSuccessor of head if it needs         * signal. But if it does not, status is set to PROPAGATE to         * ensure that upon release, propagation continues.         * Additionally, we must loop in case a new node is added         * while we are doing this. Also, unlike other uses of         * unparkSuccessor, we need to know if CAS to reset status         * fails, if so rechecking.         */        for (;;) {            Node h = head;            if (h != null && h != tail) {                int ws = h.waitStatus;                if (ws == Node.SIGNAL) {                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))                        continue;            // loop to recheck cases                    unparkSuccessor(h);                }                else if (ws == 0 &&                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))                    continue;                // loop on failed CAS            }            if (h == head)                   // loop if head changed                break;        }    }
锁的释放,比较简单,代码@1,主要是将当前线程所持有的锁的数量信息得到(从firstReader或cachedHoldCounter,或readHolds中获取 ),然后将数量减少1,如果持有数为1,则直接将该线程变量从readHolds ThreadLocal变量中移除,避免垃圾堆积。
代码@2,就是在无限循环中将共享锁的数量减少一,在释放锁阶段,只有当所有的读锁,写锁被占有,才会去执行doReleaseShared方法。
2.2 WriterLock 源码分析
2.2.1 lock方法详解
public void lock() {            sync.acquire(1);        }public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();}对上述代码是不是似曾相识,对了,在学习ReentrantLock时候,看到的一样,acquire是在AbstractQueuedSynchronizer中,关键是在 tryAcquire方法,是在不同的子类中实现的。那我们将目光移到ReentrantReadWriterLock.Sync中protected final boolean tryAcquire(int acquires) {            /*             * Walkthrough:             * 1. If read count nonzero or write count nonzero             *    and owner is a different thread, fail.             * 2. If count would saturate, fail. (This can only             *    happen if count is already nonzero.)             * 3. Otherwise, this thread is eligible for lock if             *    it is either a reentrant acquire or             *    queue policy allows it. If so, update state             *    and set owner.             */            Thread current = Thread.currentThread();            int c = getState();            int w = exclusiveCount(c);            if (c != 0) {                                   // @1                // (Note: if c != 0 and w == 0 then shared count != 0)                if (w == 0 || current != getExclusiveOwnerThread())                //@2                    return false;                if (w + exclusiveCount(acquires) > MAX_COUNT)                                  throw new Error("Maximum lock count exceeded");                // Reentrant acquire                setState(c + acquires);                                                             //@3                return true;            }            if (writerShouldBlock() ||                                                                               !compareAndSetState(c, c + acquires))                                   //@4                return false;            setExclusiveOwnerThread(current);                                             //@5            return true;        }
代码@1,如果锁的state不为0,说明有写锁,或读锁,或两种锁持有
代码@2,如果写锁为0,再加上c!=0,说明此时有读锁,自然返回false,表示只能排队去获取写锁
               如果写锁不为0,如果持有写锁的线程不为当前线程,自然返回false,排队去获取写锁。
代码@3,表示,当前线程持有写锁,现在是重入,所以只需要修改锁的额数量即可。
代码@4,表示,表示通过一次CAS去获取锁的时候失败,说明被别的线程抢去了,也返回false,排队去重试获取锁。
代码@5,成获取写锁后,将当前线程设置为占有写锁的线程。尝试获取锁方法结束。如果该方法返回false,则进入到acquireQueue方法去排队获取写锁,写锁的获取过程,与ReentrantLock获取方法一样,就不过多的解读了。
    读写锁的实现原理就分析到这了,走过路过的朋友,欢迎拍砖讨论。







      

3 0
原创粉丝点击