【Java多线程】ReentrantReadWriteLock
概述
ReentrantReadWriteLock是Lock的另一种实现方式,ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。
所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为 如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。
线程进入读锁的前提条件:
没有其他线程的写锁,
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。
线程进入写锁的前提条件:
没有其他线程的读锁
没有其他线程的写锁
使用
示例一:利用重入来执行升级缓存后的锁降级
1 class CachedData { 2 Object data; 3 volatile boolean cacheValid; //缓存是否有效 4 ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 5 6 void processCachedData() { 7 rwl.readLock().lock(); //获取读锁 8 //如果缓存无效,更新cache;否则直接使用data 9 if (!cacheValid) { 10 // Must release read lock before acquiring write lock 11 //获取写锁前须释放读锁 12 rwl.readLock().unlock(); 13 rwl.writeLock().lock(); 14 // Recheck state because another thread might have acquired 15 // write lock and changed state before we did. 16 if (!cacheValid) { 17 data = ... 18 cacheValid = true; 19 } 20 // Downgrade by acquiring read lock before releasing write lock 21 //锁降级,在释放写锁前获取读锁 22 rwl.readLock().lock(); 23 rwl.writeLock().unlock(); // Unlock write, still hold read 24 } 25 26 use(data); 27 rwl.readLock().unlock(); //释放读锁 28 } 29 }
示例二:使用 ReentrantReadWriteLock 来提高 Collection 的并发性
通常在 collection 数据很多,读线程访问多于写线程并且 entail 操作的开销高于同步开销时尝试这么做。
1 class RWDictionary { 2 private final Map<String, Data> m = new TreeMap<String, Data>(); 3 private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 4 private final Lock r = rwl.readLock(); //读锁 5 private final Lock w = rwl.writeLock(); //写锁 6 7 public Data get(String key) { 8 r.lock(); 9 try { return m.get(key); } 10 finally { r.unlock(); } 11 } 12 public String[] allKeys() { 13 r.lock(); 14 try { return m.keySet().toArray(); } 15 finally { r.unlock(); } 16 } 17 public Data put(String key, Data value) { 18 w.lock(); 19 try { return m.put(key, value); } 20 finally { w.unlock(); } 21 } 22 public void clear() { 23 w.lock(); 24 try { m.clear(); } 25 finally { w.unlock(); } 26 } 27 }
实现原理
ReentrantReadWriteLock 也是基于AQS实现的,它的自定义同步器(继承AQS)需要在同步状态(一个整型变量state)上维护多个读线程和一个写线程的状态,使得该状态的设计成为读写锁实现的关键。如果在一个整型变量上维护多种状态,就一定需要“按位切割使用”这个变量,读写锁将变量切分成了两个部分,高16位表示读,低16位表示写。
转自:https://www.cnblogs.com/zaizhoumo/p/7782941.html,https://blog.csdn.net/prestigeding/article/details/53286756