ReentrantReadWriteLock源码分析(JDK 1.7)

来源:互联网 发布:海报设计用什么软件 编辑:程序博客网 时间:2024/05/04 17:03

ReentrantReadWriteLock是ReadWriteLock接口的具体实现类。

ReadWriteLock描述:
ReadWriteLock维护了一组相对应的锁。只读锁和写锁。只要没有写操作,那么只读锁可以同时被多个线程所持有(并发访问共享数据)。写锁是独占锁,同一时刻只允许一个线程持有。

所有的ReadWriteLock实现类一定要保证writeLock操作的内存同步效果。如果一个线程成功的获取到读锁,那么它能看到先前写锁修改的所有数据。
在大量并发下操作共享数据,由于读写锁只有在修改数据时候才是互斥模式,在读时候允许多个线程并发读取。所以读写锁比互斥锁吞吐量高。与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。

相对于互斥锁来说,读写锁是否能够提升性能取决于读数据和写数据的消耗时间比例、读数据和写数据相互竞争的线程数量。举个例子:一个集合在初始化时候被填充好数据,然后频繁读取数据却很少修改数据,这时 读写锁是个很好的选择。然而,如果是修改数据频繁的话,那么大部分时间读写锁都处于互斥模式。所以它几乎是没有性能提升的。更深入一点来说,由于读写锁的实现的内部实现相对互斥锁来说更为复杂,如果使用读写锁在修改数据频繁(读数据占有比例低)的场景中,那么读写锁大部分的消耗时间都是在处理读写锁内部运作的开销了。

虽然读写锁的基本操作是比较简单的,但是在实现类内部实际上必须得作出许多决策。这些决策会影响读写锁在应用里的有效性。
策略例子:
1.当writer(可以理解为执行修改动作的线程)刚释放完写锁,而writers和readers都在等待执行,这时需要决定优先执行读锁还是写锁。通常都是写锁优先级更高,因为在读写锁的使用场景里,通常写锁都是不频繁使用的,且执行时间比较短。而读锁通常优先级靠后,因为它还使写锁需要等待相当长的一段时间才能被运行执行。或者,完全公平也可以。按照入队的先后顺序来决定它们的执行顺序。

2.当读锁处于活动状态时候,而写锁处于等待状态时,决定reader请求的读锁是否被执行。读锁的优先级会被无限期延后,等待写锁执行完毕。

3.决定锁是否可重入:一个写锁的线程是否可以重新获取写锁?当处于写锁时候,是否能获取读锁?读锁能否重入?
4.可以将写锁在不允许其他 writer 干涉的情况下降级为读取锁吗?可以优先于其他等待的 reader 或 writer 将读取锁升级为写入锁吗?

读写锁接口只有两个方法
Lock readLock(); //获取读锁

Lock writeLock();    //获取写锁

下面看看ReentrantReadWriteLock,它是ReentantLock的实现类
ReentrantReadWriteLock的特性:

获取顺序
该类并不强制指定reader或者writer获取相关锁的优先级。它提供一个关于公平策略的可选参数来让用户决定锁的获取优先 级。
非公平模式(默认)
当使用非公平模式构造函数时(默认无参构造是非公平),读锁和写锁的执行顺序是未被指定的,也会受到重入的限制。由于非公平会连续不断的竞争,会导致writer线程和reader线程被无限期的延迟。但是非公平锁的吞吐量要高于公平锁的吞吐量。
公平模式
当使用公平模式的构造函数时,会根据线程大概到达的顺序来决定线程竞争的胜出者。 当前持有的锁被释放时,会优先执行以下两种情况
1.等待最长时间的writer线程将会被分配写锁
2.一组reader线程的等待时间长于所有的writer线程,该组reader线程将会被分配读锁。
当前写锁被持有或者writrer线程正在等待,那么如果一个线程要获取公平读锁(非阻塞)将会被阻塞,直到当前等待时间最长的writer线程获取到写锁且释放写锁完毕。当然,writer线程放弃等待且写锁是处于空闲的话,如果还有reader线程正在队列等待的话,那么则会为这些reader线程分配读锁。
当一个线程尝试获取一个公平写锁时(非重入),如果当前写锁或者读锁处于被持有的话,那么该线程会阻塞直至写锁或读锁被释放。

重入
该锁允许reader和writer已持有锁情况下再次重新获取锁读锁或者写锁,获取方式如ReentrantLock独占锁一样。如果存在writer线程还持有写锁,那么非重入的reader线程 无法获取读锁。writer可以获取到读锁,但是reader不能获取写锁。
reader和writer的重入次数都被限制不得超过65535,超过则会抛出Error.

锁降级
写锁能降级为读锁,但是读锁不能升级为写锁。

锁获取中断

写锁支持Condition,读锁不支持。

在进入ReentrantReadWriteLock源码之前,需要先了解下,关于 位运算的知识。下面我简单讲讲满足看ReentrantReadWriteLock的位运算相关知识。
<< 左移运算,十进制转为二进制后,然后高位左移N位,低位补0。
如: 5 << 2
二进制:
5 = 0000 0000 0000 0000 0000 0000 0000 0101
左移两位 5 = 00 0000 0000 0000 0000 0000 0000 010100
在数学运算上也等于 = 数字M左移N位 = 数字M * 2的N次方

右移运算,十进制转为二进制后,然后低位右移N位,。 右移的规则只记住一点:符号位不变,左边补上符号位
如: 5 << 2
二进制:
5 = 0000 0000 0000 0000 0000 0000 0000 0101
右移两位 5 = 0000 0000 0000 0000 0000 0000 0000 0001
在数学运算上也等于 = 数字M右移N位 = 数字M / 2的N次方

无符号右移,和右移差不多。只是右移,符号位补0.

按位与运算符(&)(抄袭别人的)

参加运算的两个数据,按二进制位进行“与”运算。
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:两位同时为“1”,结果才为“1”,否则为0
例如:3&5 即 0000 0011 & 0000 0101 = 0000 0001 因此,3&5的值得1。

65535的二进制等于1111111111111111,那么只要是小于等于65535的和他进行与运算,都得到该数本身。

两个构造函数
1.无参
2.带有 支持自定义公平和非公平模式参数

由于Sync继承AbstractQueuedSynchronizer ,所以读者真的想深入了解ReentrantReadWriteLock,建议先深入了解 AbstractQueuedSynchronizer 。否则你也只能知其然而不知其所以然。
已经深入了解AQS的读者可以看下面。整个ReentrantReadWriteLock,只需重点关注核心Sync就可以了。

重入读写锁的核心Sync集成了AQS接口。

ReentrantReadWriteLock顾名思义,读写锁。那么它则有读锁和写锁。
说是两个锁,但实际上它只继承了个AQS,然后利用AQS的共享模式和
互斥模式来实现两个锁的语义。

既然是两个锁,那么ReentrantReadWriteLock肯定得知道这两个锁的计数。
计数:计数就是类似重入独占锁里的AQS的state,利用该state来实现控制独占锁。

可是AQS只有一个state啊!!

解决办法:由于state是个32位的int值,因此可以利用位运算把这32位的state分为两个16位的无符号short来分别存储读锁和写锁的计数。
高16位:共享锁的计数
低16位:独占锁的计数

abstract static class Sync extends AbstractQueuedSynchronizer {        private static final long serialVersionUID = 6317671515068378041L;       /*        下面是 定义 读锁和写锁 计数的一些常量和方法。         在ReentrantReadWriteLock里,由于涉及到两个锁。一个是读锁 ,一个是写锁。         而 ReentrantReadWriteLock得核心Sync集成AQS,了解过AQS的都知道,在         AQS里,都是通过控制state来实现同步语义的。而state只是个int值(或者long),         因此如果ReentrantReadWriteLock想通过AQS的state来分别得出读锁和写锁的         计数时,那么只能把该32位的int的state划分为高16位和低16位的无符号short了。         在ReentrantReadWriteLock里。 state的低16位代表互斥锁(写锁)的计数。高16位        代表共享锁(读锁)的计数。       */        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;        /** 传入AQS的state,获取该state的高16位。也就是 读锁 的计数  */        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }        /** 传入AQS的state,获取该state的低16位。也就是 写锁 的计数  */        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }        /**        为每个线程记录读锁的重入次数。该内部类的实例由ThreadLocalHoldCounter来维护。        **/        static final class HoldCounter {            int count = 0;            // Use id, not reference, to avoid garbage retention            //为了避免GC垃圾收集时候,还存在无意义引用,导致GC无效,所以使用线程的ID,而不是线程的引用。            final long tid = Thread.currentThread().getId();        }        /**         * ThreadLocal subclass. Easiest to explicitly define for sake         * of deserialization mechanics.         */        static final class ThreadLocalHoldCounter            extends ThreadLocal<HoldCounter> {            public HoldCounter initialValue() {                return new HoldCounter();            }        }        /**            可以通过该成员变量获取当前线程的读锁的重入次数。该成员变量只会在构造函数或者反序列化        时(readObject方法)被初始化。当读锁的重入次数降到0时候,那么会从该成员变量中删除        当前线程的HoldCounter实例        **/        private transient ThreadLocalHoldCounter readHolds;        /**        cachedHoldCounter主要是用来存储上最后一个(可能是上一个)成功获取读锁的线程。        它的主要作用是用来减少从ThreadLocl.get方法获取HoldCounter ,经过和别人讨论,        这也许是作者对ReentrantReadWriteLock的一些细节优化吧。        **/        private transient HoldCounter cachedHoldCounter;        /**        firstReader 代表第一个获取读锁的线程(这里的第一个指的是:可能是首次获取读锁线程,可能是在写锁释放后的首次获取的读锁线程。        更确切的说,指的是第一个把读锁的计数(shared count)从0改为1的线程。      )。        firstReaderHoldCount 是firstReader 线程的重入次数        除非线程没有释放读锁(tryReleaseShared时候会set firstReader =null),否则该行为不会引起垃圾滞留。        **/        private transient Thread firstReader = null;        private transient int firstReaderHoldCount;        Sync() {            readHolds = new ThreadLocalHoldCounter();            setState(getState()); // ensures visibility of readHolds        }        /*         * Acquires and releases use the same code for fair and         * nonfair locks, but differ in whether/how they allow barging         * when queues are non-empty.         */        /**         * Returns true if the current thread, when trying to acquire         * the read lock, and otherwise eligible to do so, should block         * because of policy for overtaking other waiting threads.         */        abstract boolean readerShouldBlock();        /**         * Returns true if the current thread, when trying to acquire         * the write lock, and otherwise eligible to do so, should block         * because of policy for overtaking other waiting threads.         */        abstract boolean writerShouldBlock();        /**        由于ReentrantReadWriteLock支持Condition,而Condition的wait会调用        tryRelease 释放AQS的state(该state分为高低16位,包括读锁和写锁的计数)。        还会调用tryAcquire来重新设置之前tryRelease 的state(具体细节可以参考我的AQS源码分析)        **/        protected final boolean tryRelease(int releases) {            if (!isHeldExclusively())                throw new IllegalMonitorStateException();            int nextc = getState() - releases;            /**            注意这是独占模式下的释放方法。              boolean free = exclusiveCount(nextc) == 0;            获取写锁的state(低16位),然后判断state是否==0。是则认为写锁完全释放完成。            **/            boolean free = exclusiveCount(nextc) == 0;            if (free)                setExclusiveOwnerThread(null);            setState(nextc);            return free;        }        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);            //这里先判断AQS的state是否等于0,如果不等于0,则有可能当前时间读锁或者写锁被持有            if (c != 0) {                // (Note: if c != 0 and w == 0 then shared count != 0)                /**                这里为什么判断写锁的计数等于0,则返回false呢???                注意上面的先行条件,c!=0。如果不等于0,则有可能当前时间读锁或者写锁被持有。那么                w==0则代表当前持有的是读锁。所以理所当然需要返回false。读锁处于活动中,写锁退让等待。                或者!!                如果当前持有的是写锁,但是非当前线程的话,那么也可以返回false,毕竟修改数据只能同时允许一个线程的存在.                **/                if (w == 0 || current != getExclusiveOwnerThread())                    return false;                //判断写锁重入次数是否大于65535                if (w + exclusiveCount(acquires) > MAX_COUNT)                    throw new Error("Maximum lock count exceeded");                // Reentrant acquire                setState(c + acquires);                return true;            }            /**            writerShouldBlock用于判断writer线程和reader线程同时等待情况时,应该优先获取哪个?            公平锁和非公平锁对writerShouldBlock都进行了重写。            公平锁:判断AQS同步等待队列的头结点是否有正在等待的线程,且等待线程非当前线程。如果存在,那么返回false,顺序等待执行。            非公平锁:直接返回false,以竞争的形式来争夺锁获取            **/            if (writerShouldBlock() ||                !compareAndSetState(c, c + acquires))                return false;            setExclusiveOwnerThread(current);            return true;        }        protected final boolean tryReleaseShared(int unused) {            Thread current = Thread.currentThread();            if (firstReader == current) {                // assert firstReaderHoldCount > 0;                if (firstReaderHoldCount == 1)                    firstReader = null;                else                    firstReaderHoldCount--;            } else {                HoldCounter rh = cachedHoldCounter;                    //可以注意到cachedHoldCounter仅仅只是为了减少从readHolds.get();。但是我看了                //ThreadLocal的源码,get方法消耗并不算大,也许这是作者对该锁的一些细节优化,使其尽可能的快。                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;            }            for (;;) {                int c = getState();                //因为使用state的高16位为读锁的state,所以在获取或者释放时候,都是操作SHARED_UNIT,而不是unused(1)                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;            }        }        private IllegalMonitorStateException unmatchedUnlockException() {            return new IllegalMonitorStateException(                "attempt to unlock read lock, not locked by current thread");        }        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();            //判断当前写锁是否被持有 且 持有写锁的线程不是当前线程。则返回-1排队等待            if (exclusiveCount(c) != 0 &&                getExclusiveOwnerThread() != current)                return -1;            int r = sharedCount(c);            /**            readerShouldBlock是在读锁lock时候,需要首先判断是否要阻塞当前读锁的获取。            readerShouldBlock有两种实现:            公平锁:判断AQS同步等待队列的头结点是否有正在等待的线程,且等待线程非当前线程。如果存在,那么返回true,!true=false,顺序排队等待执行。            非公平锁:判断当前头节点线程是否是writer(互斥模式),如果是,则返回true,!true=false,顺序排队等待执行            **/            if (!readerShouldBlock() &&                r < MAX_COUNT &&                compareAndSetState(c, c + SHARED_UNIT)) {                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++;                }                return 1;            }            return fullTryAcquireShared(current);        }        /**         * 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) {                    if (getExclusiveOwnerThread() != current)                        return -1;                    // else we hold the exclusive lock; blocking here                    // would cause deadlock.                } 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 != 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)) {                    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;                }            }        }        /**       tryWriteLock方法是由写锁的tryLock调用的。该方法和tryAcquire 功能几乎相同,仅仅只是去掉了调用writerShouldBlock的"效果"        **/        final boolean tryWriteLock() {            Thread current = Thread.currentThread();            int c = getState();            if (c != 0) {                int w = exclusiveCount(c);                if (w == 0 || current != getExclusiveOwnerThread())                    return false;                if (w == MAX_COUNT)                    throw new Error("Maximum lock count exceeded");            }            if (!compareAndSetState(c, c + 1))                return false;            setExclusiveOwnerThread(current);            return true;        }        /**        tryReadLock方法是由读锁的tryLock调用的。该方法和tryAcquireShared 功能几乎相同,仅仅只是去掉了调用readerShouldBlock的"效果"        **/        final boolean tryReadLock() {            Thread current = Thread.currentThread();            for (;;) {                int c = getState();                if (exclusiveCount(c) != 0 &&                    getExclusiveOwnerThread() != current)                    return false;                int r = sharedCount(c);                if (r == MAX_COUNT)                    throw new Error("Maximum lock count exceeded");                if (compareAndSetState(c, c + SHARED_UNIT)) {                    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++;                    }                    return true;                }            }        }        protected final boolean isHeldExclusively() {            // While we must in general read state before owner,            // we don't need to do so to check if current thread is owner            return getExclusiveOwnerThread() == Thread.currentThread();        }        // Methods relayed to outer class        final ConditionObject newCondition() {            return new ConditionObject();        }        final Thread getOwner() {            // Must read state before owner to ensure memory consistency            return ((exclusiveCount(getState()) == 0) ?                    null :                    getExclusiveOwnerThread());        }        //获取读锁被获取的总次数        final int getReadLockCount() {            return sharedCount(getState());        }        final boolean isWriteLocked() {            return exclusiveCount(getState()) != 0;        }        final int getWriteHoldCount() {            return isHeldExclusively() ? exclusiveCount(getState()) : 0;        }        //获取当前线程的重入次数        final int getReadHoldCount() {            if (getReadLockCount() == 0)                return 0;            Thread current = Thread.currentThread();            if (firstReader == current)                return firstReaderHoldCount;            HoldCounter rh = cachedHoldCounter;            if (rh != null && rh.tid == current.getId())                return rh.count;            int count = readHolds.get().count;            //因为当ThreadLocal.get时候,如果不存在初始化对象,则会调用initialValue初始化,所以如果==0,要删除            if (count == 0) readHolds.remove();            return count;        }        /**         * Reconstitute this lock instance from a stream         * @param s the stream         */        private void readObject(java.io.ObjectInputStream s)            throws java.io.IOException, ClassNotFoundException {            s.defaultReadObject();            readHolds = new ThreadLocalHoldCounter();            setState(0); // reset to unlocked state        }        final int getCount() { return getState(); }    }
0 0
原创粉丝点击