很“抠”性能的 Java 读写锁

本文详细分析了 Java 中的读写锁机制,特别是 ReentrantReadWriteLock 的源码,探讨了读写锁在读多写少场景下的优势。内容包括并发读写、锁降级与不支持锁升级的概念,以及公平锁和非公平锁的工作原理。通过对源码的解析,揭示了读写锁在性能优化上的策略,如避免不必要的锁竞争。
摘要由CSDN通过智能技术生成
源码版本:OpenJDK 11

读写锁

之前分析过并发场景下高频使用的 Reentrantlock 源码解析 (juejin.cn),该类实现 Lock接口,以达到独占获取的语义。

而这次分析ReentrantReadWriteLock 实现的是 ReadWriteLock接口。

ReadWriteLock 我理解为是 Lock在特定场景下的扩展,当然我们都知道这个场景就是读多写少。在读多写少的场景下,如果依旧是独占式获取资源,很显然会出现性能瓶颈。

ReadWriteLock所表达的读写锁语义是在同一时刻允许被多个读访问,但是如果是写访问,则其他的写与读都会被阻塞image-20210803102404332

至于为啥说ReadWriteLockLock在读多写少场景下的扩展,因为接口唯二的两个方法的返回值都是 Lock啊,果然锁的大爹还得是 Lock

public interface ReadWriteLock {
    
    Lock readLock();
    Lock writeLock();
}

读写锁的用处

比较典型读多写少场景想来应该是缓存了。项目里面使用缓存主要是为了提高访问某些资源的速度,常见的就是应用和数据库之间加一个缓存。

但是直接使用缓存会带来某些不一致的问题:

  • 不同的缓存更新策略会带来不同的一致性问题

    (这里列出了常见的缓存更新策略,有兴趣的可以自行查一下)

    • Cache Aside Pattern(旁路缓存模式)
    • Read/Write Through Pattern(读写穿透)
    • Write Behind Pattern(异步缓存写入
  • 缓存失效时,可能大量的读请求落到数据库击穿,造成缓存击穿

如果是对一致性要求比较严格的情况,可以考虑使用读写锁。或避免缓存击穿,但是一般典型情况下可能只在设置缓存的节点直接使用独占锁。

写个假缓存的demo

public class ReadWriteLockDemo {
   
    private Integer cache;
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public Integer getFromCache() {
   
        lock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + ":开始读取缓存");
        Integer res = cache;
        System.out.println(Thread.currentThread().getName() + ":结束读取缓存");
        lock.readLock().unlock();
        if (cache == null) {
   
            return loadCache();
        }
        return cache;
    }

    private Integer loadCache() {
   
        lock.writeLock().lock();
        Integer val;
        if (cache == null) {
   
            System.out.println(Thread.currentThread().getName() + ":开始加载数据库");
            // get from database...
            _load();
            val = new Random(5).nextInt();
            cache = val;
            System.out.println(Thread.currentThread().getName() + ":结束加载数据库");
            lock.writeLock().unlock();
        } else {
   
            lock.readLock().lock();
            lock.writeLock().unlock();
            System.out.println(Thread.currentThread().getName() + ":开始读取缓存");
            val = cache;
            System.out.println(Thread.currentThread().getName() + ":结束读取缓存");
            lock.readLock().unlock();
        }
        return val;
    }

    private void _load() {
   
        // 模拟读写数据库的耗时
        try {
   
            TimeUnit.SECONDS.sleep(1L);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }

    private void setCache(Integer val) {
   
        lock.writeLock().lock();
        System.out.println(Thread.currentThread().getName() + ":开始写缓存"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值