ReentrantReadWriteLock读锁作用

本文探讨了读写锁出现的原因,主要是为了在多线程环境中提高并发性能,尤其是在读操作频繁时。读锁的作用在于防止读取到写操作的中间状态,确保数据一致性。读写锁的特点是读读共享,其他情况互斥。锁降级是其特性之一,即写锁降级为读锁。适用场景为读多写少的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

读写锁的出现原因

ReentrantLock实现一种标准的互斥锁,每次最多只有一个线程能持有ReentrantLock,限制了并发性,互斥是一种保守的加锁策略,虽然避免了“写/写”冲突和“写/读”冲突,但也避免了“读/读”冲突。

而大部分情况下读操作比较多,如果此时能够放宽加锁需求,允许多个读操作的线程同时访问数据结构,可以提升程序的性能(只要每个线程保证读取到最新的数据,并且在读取数据时不会有其他线程修改数据就行)

读锁的作用

任何锁表面上是互斥,但本质是都是为了避免原子性问题(如果程序没有原子性问题,那只用volatile来避免可见性和有序性问题就可以了,效率更高),读锁自然也是为了避免原子性问题。

比如一个long型参数的写操作并不是原子性的,如果允许同时读和写,那读到的数很可能是就是写操作的中间状态,比如刚写完前32位的中间状态。long型数都如此,而实际上一般读的都是复杂的对象,那中间状态的情况就更多了。

所以读锁是防止读到写的中间值。

读写锁的特点

  • 如果有一个线程已经占用了读锁,则此时其他线程如果要申请读锁,可以申请成功。

  • 如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁,因为读写不能同时操作。

  • 如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,都必须等待之前的线程释放写锁,同样也因为读写不能同时,并且两个线程不应该同时写。

  • 锁降级 不可升级 从写锁降级到读锁不可升级(获取写锁->获取读锁->释放写锁)

总结

  • 读读共享、其他都互斥(写写互斥、读写互斥、写读互斥)

  • 加读锁是防止读取到中间值

示例代码

ReentrantReadWriteLock源码上提供了两段demo代码

  1. 下面代码展示了如何在更新缓存后执行锁降级
class CachedData {
    Object data;
    volatile boolean cacheValid;
    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    void processCachedData() {
        rwl.readLock().lock();
        if (!cacheValid) {
            // 获取写锁前必须释放读锁
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            try {
                // 双重校验 其他线程可能会获取到读锁并修改值
                if (!cacheValid) {
                    // data = ... // 这里可以修改data值,一般是db层操作
                    cacheValid = true;
                }
                // 释放写锁前获取读锁 实现锁降级
                rwl.readLock().lock();
            } finally {
                rwl.writeLock().unlock(); // 释放写锁,仍持有读锁
            }
        }
        try {
            // use(data); // 使用数据 最后释放读锁
        } finally {
            rwl.readLock().unlock();
        }
    }
}
  1. 可用于在某些集合的某些用途中提高并发性,特别适合读多写少的情景。以TreeMap为例
class RWDictionary {
    private final Map<String, Object> m = new TreeMap<>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Object get(String key) {
        r.lock();
        try {
            return m.get(key);
        } finally {
            r.unlock();
        }
    }

    public String[] allKeys() {
        r.lock();
        try {
            return m.keySet().toArray(new String[]{});
        } finally {
            r.unlock();
        }
    }

    public Object put(String key, Object value) {
        w.lock();
        try {
            return m.put(key, value);
        } finally {
            w.unlock();
        }
    }

    public void clear() {
        w.lock();
        try {
            m.clear();
        } finally {
            w.unlock();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值