【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

posted @ 2018-11-15 11:50  是谁扭曲了时空  阅读(169)  评论(0编辑  收藏  举报