实现?
ReentrantReadWriteLock并没有继承ReentrantLock,也并没有实现Lock接口,而是实现了ReadWriteLock接口,该接口提供readLock()方法获取读锁,writeLock()获取写锁。
如何理解?
读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为 如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。
ReentrantReadWriteLock按照字面意思是读写锁,如果你把它理解为对IO的控制,那就大错特错了(其实大多数人的直觉是这样)。其实你只要把它理解成一个数据库的事务锁就对了。众所周知数据库事务锁的特点就是,读写分离。而ReentrantReadWriteLock是类似最高级的事务级别Serializable可串行化(严格讲比这个还更严谨)。什么意思呢,意思就是,对一条数据的更新操作只影响其它对该条数据的更新操作,而读操作是不影响的。 而并发锁Lock也好,synchronizy也好,是直接把读写都锁住的。就是说,该代码块一但锁住之后,既不能读也不能写。 但这样是有问题的,有些线程只是想读取一下数据,我又不改数据,你锁它干嘛呢?(类似事物吧) 所以ReentrantReadWriteLock把锁拆分成了读锁和写锁。 写锁之间的互斥的,但读锁不互斥(大家一起读数据么,压根就没冲突)。 但是有一点要注意。就是你想获取写锁,是除当前线程外,不能存在其它的读锁的。 好比就是说,我要改里面的数据了,那些获取了读锁的线程,必须通通退出来,否则会出现读到老数据的问题(类似事物里面的脏读) ,获取到写锁之后,其它线程也不能再获取到读锁了。
有何作用?
既然给并发锁分成了读写两个阶段,那Lock之后,在某些场景下,也能还能访问到Lock里面的代码块的。 等于开了个特定条件可以访问。 这样有什么好处呢? 好处就是,特定条件下,提高了性能: 本来都不能访问的数据,现在可以访问了,不用傻傻的全部排队等待互斥锁(当然这前提是要理解好和用好这个锁的含义)
特性?
特性有很多博文都写了很多也很好的例子,我这里就不重复造轮子了(大家自行度娘),这里只是简单几句话阐述该类的作用,方便大家理解而已。
场景?
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。
例如,以下是转他人总结的的一部分代码示例,一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}
锁降级?
//如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理及部分代码)
class CachedData {
Object data;
//保证状态可见性
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// 在获取写锁前必须释放读锁
rwl.readLock().unlock();
rwl.writeLock().lock();
//再次检查其他线程是否已经抢到
if (!cacheValid) {
//获取数据
data = ...
cacheValid = true;
}
// 在释放写锁之前通过获取读锁来降级
rwl.readLock().lock();
//释放写锁,保持读锁
rwl.writeLock().unlock();
}
use(data);
rwl.readLock().unlock();
}
}
源码详解可参考:https://blog.csdn.net/prestigeding/article/details/53286756