zoukankan      html  css  js  c++  java
  • 从火车站车次公示栏来学Java读写锁

    Java多线程并发之读写锁

    本文主要内容:读写锁的理论;通过生活中例子来理解读写锁;读写锁的代码演示;读写锁总结。通过理论(总结)-例子-代码-然后再次总结,这四个步骤来让大家对读写锁的深刻理解。

    本篇是《凯哥(凯哥Java:kagejava)并发编程学习》系列之《Lock系列》教程的第七篇:《Java并发包下锁学习第七篇:读写锁》。

    一:读写锁的理论

    什么是读写锁?

    多个线程同时读一个资源类是没有任何问题的,所以为了满足在并发的情况下,读取共享资源应该是可以同时进行的;但是,如果一个线程想要去写共享资源,就不应该再有其他线程可以对该共享资源进行读或者是写操作了。

    即读写锁在同一时刻可以允许多个多线程访问,但是在写线程访问的时候,所有的读线程和其他写线程都会被阻塞。读写锁实际维护了一对锁,一个读锁,一个写锁,通过分离读锁和写锁,使得其并发性比独占式锁(排他锁)有了很大的提升。

    为什么需要读写锁?

    通过前面文章的学习,我们知道了ReentrantLock(下文简称:RLock)对象了。Rlock比起synchronized(下文简称Sync)来说有三个优点:RLock可以被中断;RLock可以有公平锁;RLock可以绑定多个条件。那么既然RLock比Sync有这么多优点,为什么还需要读写锁呢?

    那是因为RLock是独占式(排他) 锁,即当线程1获取到资源的时候,其他线程不能再来操作共享资源了。就算是RLock的操作是读取的时候,其他线程也不能读取共享资源的操作。这在现实生活中是不符合逻辑的(在下文神话中读写锁的例子中我们就能体会到为什么不符合逻辑的),而且性能也比较慢。所以就有了读写锁的出现。

    二:读写锁的理解

    生活中读写锁的例子

    例子一:我们大家去火车站乘车的时候,有个大大的公示屏幕,会告诉大家当前车次是否晚点。显示屏是给给所有乘客看的,如果火车晚点,对应车次后面就会被修改成晚点大约xxx分钟。这个修改的动作只能是火车站内部人员来操作的,我们乘客是不能操作的。这个过程,站在并发角度来分析的的话:电子屏幕是共享数据;千千万万的乘客是不同的线程;火车站内部工作人员也是不同的线程;乘客是读资源的线程,当一个线程来读取的时候,其他线程也可以读取操作的;火车站内部工作人员修改火车信息的时候,同时只能有一个工作人员来修改,不能两个都来修改。如果两个都来修改的话,上一秒显示晚点1min,下一秒显示正常。这个是不行的,乘客有可能会错过乘车的。所以修改的时候同时只能由一个工作人员来修改。

    例子二:我们在玩王者荣耀的时候,有时候会遇到停服更新的。在不更新前,所有玩家都可以玩,当停服更新的时候,所有玩家就不能玩了。这个操作在并发角度来说:千千万万的玩家是读共享资源的;游戏维护者是写操作的。当停服更新的时候,读操作就被阻塞了,只能等写操作,也就是更新完成后,才可以接着玩。

    通过上面两个例子我们可以分析到读写锁的三个参与者:共享资源;读对象;写对象。而且读和写一般是分离的。

    三:读写锁的代码演示

    我们就用火车站进站案例来模拟:

    未使用锁的时候

    先来看看屏幕对象:

    再来看看多个工作人员更新操作及多个乘客获取操作:

    查看运行结果:

    从运行结果中,我们可以发现当工号未13的还没有更新完车次信息的时候,工号12和14的员工也来更新了。这种操作是不允许的。因为写操作要原子性,要独占。当工作人员甲在修改的时候车次信息的时候,其他工作人员不能同时修改同一个车次信息了。而且从乘客获取车次信息的数据来看,获取到的只是工号是13的。这个时候获取到的数据不一定是正确的了。所以,不使用锁是不行的。

    使用排他锁

    如果使用独占式做的话,我们查看运行结果:

    从运行结果来看,再读取的时候,需要一个一个读取的。当16号乘客查看的时候,17号乘客是不能查看的。这个是不符合实际业务逻辑的。所以,独占式(排他锁)RLock在这里不适合。我们再来看看读写锁:

    使用读写锁

    先来看看使用读写锁的屏幕对象

    再来看看运行结果:

    从运行结果中,我们可以看到,工作人员是一个一个的操作完成的。当14号操作完成之后,13号和12号才可以操作的。这个符号我们正常的业务。乘客读取的时候,读取到的都是最后一次更新,这个也符合我们的业务。所以,通过读写锁来操作车站屏幕是可以的。

    四:读写锁总结

    4.1:wrLock类对象

    同样包含了公平锁和非公平锁。

    其中ReadLock是读锁对象;WriteLock是写锁对象。

    4.2:使用语法

    读操作使用ReadLock

    编辑

    写操作的时候,使用WriteLock对象:

    4.3:总结

    读写锁(ReentrantReadWriteLock),凯哥就简写rwLock。也可以实现公平和非公平的。其内部维护了一对锁:一个读锁(ReadLock对象),一个写锁(writeLock对象),通过读写分离的方式来提高并发性能。读写锁也叫共享锁。其共享是在读数据的时候,可以让多个线程同时进行读操作的。在写的时候具有排他性,其他读或者写操作都要被阻塞。

    一般情况下,读写锁的性能都会比排他锁性能好,那是因为,大多数场景读操作多于写操作的。在读多与写的场景下,读写锁能够提供比排他锁更好的并性能和吞吐量。

        ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​欢迎来聊~

  • 相关阅读:
    读书笔记——吴军《态度》
    JZYZOJ1237 教授的测试 dfs
    NOI1999 JZYZOJ1289 棋盘分割 dp 方差的数学结论
    [JZYZOJ 1288][洛谷 1005] NOIP2007 矩阵取数 dp 高精度
    POJ 3904 JZYZOJ 1202 Sky Code 莫比乌斯反演 组合数
    POJ2157 Check the difficulty of problems 概率DP
    HDU3853 LOOPS 期望DP 简单
    Codeforces 148D. Bag of mice 概率dp
    POJ3071 Football 概率DP 简单
    HDU4405 Aeroplane chess 飞行棋 期望dp 简单
  • 原文地址:https://www.cnblogs.com/kaigejava/p/12651268.html
Copyright © 2011-2022 走看看