湖北武汉新闻专题:【〖漫画〗】「互斥锁」ReentrantLock不好用?试试读写锁ReadWriteLock

2020-05-16 40 views 0

扫一扫用手机浏览

ReentrantLock‘完善实’现了互斥,完善解决了并发问题。然则却意外发现它对于读多写少《的》场景效率着实不行。此时ReentrantReadWriteLock来救场了!一种适用于读多写少场景《的》锁,可以大幅度提升并发效率,你必须会哦!

序幕

为何引入读写锁?

ReentrantReadWriteLock,顾名思义,《是可重用《的》读》写锁。

在读多写少《的》场所,【读写锁对系统性】能是很有利益《的》。由于若是系统在读写数据时均只使用独占锁,《那么读操作和写操》作间、读操作和读操作间、写操作和写操作间均不能做到真正《的》并发,而且需要相互守候。而读操作自己不会影响数据《的》完整性和一致性。

因此,理论讲,在大部分情形下,应该可以允许多线程同时读,读写锁正是实现了这种功效。

划重点:读写锁适用于读多写少《的》情形。可以优化性能,提升易用性。

读写锁 ReadWriteLock

读写锁,『并不是』 Java 语言特有《的》,而是一个广为使用《的》通用手艺,所有《的》读写锁都遵守以下三条基本原则:

  • 允【许】多个线程同时读共享变量;
  • 只允许一个线程写共享变量;
  • 若是一个写线程正在《执行写操作》,此时克制读线程读共享变量。

读写锁与互斥锁《的》一个主要区别就是读写锁允【许】多个线程同时读共享变量,而互斥锁是不允许《的》,这是读写锁在读多写少场景下性能优于互斥锁《的》要害。但读写锁《的》写操作是互斥《的》、独占《的》,当一个线程在写共享变量《的》时刻,是不允许其他线程《执行写操作》和读操作。只要没有写操作,读取锁可以由多个读线程同时保持。读写锁接见约束如下表所示:

读写锁
非壅闭 壅闭
壅闭 壅闭

读写锁维护了一对相关《的》锁,一个用于只读操作,一个用于写入操作。

    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    //读锁
    private final Lock r = rwl.readLock();
    //写锁
    private final Lock w = rwl.writeLock();

为了对比读写锁和独占锁《的》区别,我们可以写一个测试代码,划分传入ReentrantLock 和 ReadLock,对比一下总耗时。

    private static final ReentrantLock lock = new ReentrantLock();
    private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private static final Lock r = rwl.readLock();

    public static String read(Lock lock, String key) throws InterruptedException {
        r.lock();
        try {
            // 模拟读耗时多《的》场景 更能看出区别
            Thread.sleep(1000 * 10);
            return m.get(key);
        } finally {
            r.unlock();
        }
    }

快速实现一个缓存

回忆一下工作中经常用到《的》缓存,例如缓存元数据,不就是一种典型《的》读多写少应用场景吗?缓存之《以是》能提升性能,一个主要《的》条件就是缓存《的》数据一定是读多写少《的》,例如元数据和基础数据(基本上)不会发生变化(写少),然则使用它们《的》地方却许多(读多)。

我们是不是可以用ReentrantReadWriteLock来手写一个缓存呢?先画一张图模拟简朴《的》缓存流程吧:

    String get(String key) throws InterruptedException {
        String v = null;
        r.lock();
        log.info("{}获取读锁 Time={}",Thread.currentThread().getName(),System.currentTimeMillis());
        try {
            v = m.get(key);
        } finally {
            r.unlock();
            log.info("{}【释放读锁】 time={}",Thread.currentThread().getName(),System.currentTimeMillis());
        }
        if (v != null) {
            log.info("{}缓存存在,返回效果 time={}",Thread.currentThread().getName(),System.currentTimeMillis());
            return v;
        }
        w.lock();
        log.info("{}缓存中不存在,查询数据库,获取写锁 time={}",Thread.currentThread().getName(),System.currentTimeMillis());
        try {
            log.info("{}二次验证 time={}",Thread.currentThread().getName(),System.currentTimeMillis());
            v = m.get(key);
            if (v == null) {
                log.info("{}查询数据库完成 time={} ",Thread.currentThread().getName(),System.currentTimeMillis());
                v = "value";
                log.info("-------------验证写锁占有《的》时刻 其他线程无法《执行写操作》和读操作----------------");
                Thread.sleep(1000*5);
                m.put(key, v);
            }
        } finally {
            log.info("{}写锁释放 time={}",Thread.currentThread().getName(),System.currentTimeMillis());
            w.unlock();
        }
        return v;
    }

原创声明:本文来源于微信民众号【{胖滚猪学编程}】,连续更新JAVA\大数据干货,用漫画形式让编程so easy and interesting。转载请注明出处。

ReentrantReadWriteLock《的》特色功效

在 J.U.C Lock(包之)ReentrantLock互斥锁,「我们先容了」ReentrantLock相比synchronized<《的》>几大特色功效,例如公正锁、非壅闭获取锁、超时、中止。那么ReentrantReadWriteLock是否也有呢?

简朴。。看看源码不就清晰了。以下源码都是在ReentrantReadWriteLock.java中撩出来《的》~ 剩下《的》我就不用多说了吧!若是不清晰这些方式可「以转头看看」 J.U.C Lock(包之)ReentrantLock互斥锁

    public ReentrantReadWriteLock(boolean fAIr) {
        sync = fair ? new FairSync() : new NonfairSync();
        reADErLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));
        }
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);
        }

读写锁《的》升级与降级

还想跟你聊聊锁《的》升级和降级。也许你是第一次听到,锁另有升级降级《的》功效。但实在不难理解,<好比>在读写锁中,写锁变为读锁是完全可 行《的》方案[,不会有任何问题,<这里>写锁变读锁就叫做锁《的》降级

那么可以升级吗?熟话说降级容易,你只要天天不来上班就行了,升级可难哦。锁中也是,只是在锁中加倍苛刻,完全不允许升级,即读锁无法升级为写锁必须先【释放读锁】,才可以获取写锁。为什么不允许升级?‘试想有’1000个读线程同时执行,同时升级为写锁,{会发生什}么?获取写锁《的》条件是读锁和写锁均未被占用,因此可能导致壅闭较长《的》时间,<也>可能发生死锁。

先写个代码验证一下吧,在(2)处我们实现了降级,程序是完全ok《的》,在(1)处若是你注释掉 r.unlock(),试图升级为读锁,你会发现程序会跑不下去《的》,据此可以验证我们所说《的》:读写锁可以降级、无法升级。

    void processCachedData() {
        // 获取读锁
        r.lock();
        if (!cacheValid) {
            // 【释放读锁】 由于不允许读锁《的》升级 可以注释掉该行代码 整个程序会壅闭
            r.unlock(); //(1)
            // 获取写锁
            w.lock();
            try {
                // 再次检查状态
                if (!cacheValid) {
                    data = "{胖滚猪学编程}";
                    cacheValid = true;
                }

                // 释放写锁前 降级为读锁 降级是可以《的》
                r.lock(); //(2)
            } finally {
                // 释放写锁
                w.unlock();

            }

        }
        // 此处仍然持有读锁
        try {
            System.out.println(data);
        } finally {
            r.unlock();
        }

    }

总结

读写锁适用于读多写少《的》情形。可以优化性能,提升易用性。缓存就是个很好《的》例子。

读写锁最大《的》特征是允【许】多个线程同时读共享变量。然则只允许一个线程写共享变量,且若是一个写线程正在《执行写操作》,此时克制读线程读共享变量。

ReentrantReadWriteLock读写锁类似于 ReentrantLock,【支持】公正模式和非公正模式、支持非壅闭获取锁、超时、中止等特征。然则有一点需要注重,那就是只有写锁支持条件变量,读锁是不支持条件变量《的》,读锁挪用 newCondition() 会抛出 UnsupportedOperationException 异常。

《以是》!我们必须领会种种锁《的》用途,才气在生产上选择最合适高效《的》方式。

原创声明:本文来源于微信民众号【{胖滚猪学编程}】,连续更新JAVA\大数据干货,用漫画形式让编程so easy and interesting。转载请注明出处。

本文转载自民众号【{胖滚猪学编程}】 用漫画让编程so easy and interesting!迎接关注!形象来源于微信脸色包【胖滚家族】喜欢可以下载哦~

,

诚信在线

诚信在线 www.chSJkj.cn携手www.cx11.net<合作>,期待2019年,创新、务实、奋进。

皇冠体育网站内容转载自互联网,如有侵权,联系皇冠APP下载删除。

本文链接地址:http://www.wx-gangguan.com/post/1152.html

相关文章

allbet gmaing:智慧物流 e键加速

数据泉源:国家邮政局 焦点阅读 近年来,以新一代信息技术为支持的智慧物流在我国蓬勃发展。运作治理更高效,货物运输更便捷,智慧物...

科技 2020-09-28 阅读4 评论0

发表评论