读写锁ReadWriteLock和ReentrantReadWriteLock

接口依赖图

这里写图片描述
ReentrantReadWriteLock的接口依赖图

概述

Java并发编程包提供了读写锁的实现,其维护了一对相关的锁 ,即 “读取锁”和“写入锁”,一个用于读取操作,另一个用于写入操作。

“读取锁”用于只读操作,它是“共享锁”,能同时被多个线程获取。
“写入锁”用于写入操作,它是“独占锁”,写入锁只能被一个线程锁获取。

所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为 如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。

读写锁ReadWriteLock是一个接口。ReentrantReadWriteLock是它的实现类,ReentrantReadWriteLock包括内部类ReadLock和WriteLock。

java.util.concurrent.locks.ReadWriteLock源码如下:

public interface ReadWriteLock {
    //获取一个读锁
    Lock readLock();
    //获取一个写锁
    Lock writeLock();
}

ReentrantReadWriteLock支持以下功能:

1)支持公平和非公平的获取锁的方式;

2)支持可重入。读线程在获取了读锁后还可以获取读锁;写线程在获取了写锁之后既可以再次获取写锁又可以获取读锁;

3)还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不允许的;

4)读取锁和写入锁都支持锁获取期间的中断;

5)Condition支持。仅写入锁提供了一个 Conditon 实现;读取锁不支持 Conditon ,readLock().newCondition() 会抛出 UnsupportedOperationException。

java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock.newCondition()

public Condition newCondition() {
    throw new UnsupportedOperationException();
}

java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock.newCondition()

public Condition newCondition() {
    return sync.newCondition();
}

使用

示例一:利用重入来执行升级缓存后的锁降级

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();
   }
 }

示例二:使用 ReentrantReadWriteLock 来提高 Collection 的并发性

在使用某些种类的 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(); }
    }
}

参考文档:
1. https://www.cnblogs.com/zaizhoumo/p/7782941.html
2. https://blog.csdn.net/prestigeding/article/details/53286756
3. https://blog.csdn.net/qq924862077/article/details/70303821

猜你喜欢

转载自blog.csdn.net/thebigdipperbdx/article/details/79998156