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(); } }
- ReentrantReadWriteLock源码分析(JDK 1.7)
- jdk 源码分析(9)java ReentrantReadWriteLock分析
- ReentrantReadWriteLock源码分析
- JUC - ReentrantReadWriteLock 源码分析
- Java并发-ReentrantReadWriteLock源码分析
- Java 并发 ---ReentrantReadWriteLock源码分析
- Java 1.7 ReentrantReadWriteLock源码解析
- 喻红叶《Java并发-ReentrantReadWriteLock源码分析》
- JUC源码分析13-locks-ReentrantReadWriteLock
- 【Java8源码分析】locks包-ReentrantReadWriteLock
- ReentrantReadWriteLock分析
- LockSupport源码分析(JDK 1.7)
- ReentrantLock源码分析(JDK 1.7)
- FutureTask源码分析(JDK 1.7)
- java并发锁ReentrantReadWriteLock读写锁源码分析
- Java显式锁学习总结之五:ReentrantReadWriteLock源码分析
- Java多线程系列(十一)—ReentrantReadWriteLock源码分析
- JDK 1.7之 ConcurrentHashMap 源码分析
- Android 自定义View 性能分析学习 <1>
- DataTable分组求和【UP,UP,UP】
- ios7 修改导航栏的颜色
- js操作cookie
- arc 和 非arc 中 block 的循环引用
- ReentrantReadWriteLock源码分析(JDK 1.7)
- 加载XIB的方法
- iOS 保持界面流畅的技巧(最全最详尽的了)
- Android启动过程分析(2)
- mac 安装libmemcached
- 关于STM32的外部中断服务程序
- 欢迎使用CSDN-markdown编辑器
- curl HTTP 测试常用参数总结
- sql存储过程学习实例