今天把这两个锁的内核实现源码重新捋了一遍,基于liunx2,6.0,直接粘注释版: 核心文件,x86下实现的spinlock
在Java并发包中常用的锁(如:ReentrantLock),基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时 刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得 并发性相比一般的排他锁有了很大提升。
互斥锁我们都知道会锁定代码临界区,当有一个goroutine获取了互斥锁后,任何goroutine都不可以获取互斥锁,只能等待这个goroutine将互斥锁释放,无论读写操作都会加上一把大锁,在读多写少场景效率会很低,所以大佬们就设计出了读写锁,读写锁顾名思义是一把锁分为两部分:读锁和写锁,读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的,总结来说:读读不互斥,读写互斥,写写互斥;
读写锁维护一对锁,读锁和写锁 分离读锁和写锁,并发性比排它锁有很大提升 ReadWriteLock仅定义读锁和写锁的两个方法——readLock()和writeLock() 实现类ReentrantReadWriteLock提供了以下方法: 方法 描述 int getReadLockCount() 返回当前读锁被获取的次数。该次数不等于获取读锁的线程数,比如:仅一个线程,它连续获取(重进入)了n次读锁,那么占据读锁的线程数是1,但该方法返回n int getReadHoldCount() 返回当前线程获取读
重入锁ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少。然而读服务不存在数据竞争问题,如果一个线程在读时禁止其他线程读势必会导致性能降低。所以就提供了读写锁。 读写锁维护着一对锁,一个读锁和一个写锁。通过分离读锁和写锁,使得并发性比一般的排他锁有了较大的提升:在同一时间可以允许多个读线程同时访问,但是在写线程访问时,所有读线程和写线程都会被阻塞。 读写锁的主要特性: 公平性:支持公平性和非公平性。 重入性:支持
最近做的一个小项目中有这样的需求:整个项目有一份config.json保存着项目的一些配置,是存储在本地文件的一个资源,并且应用中存在读写(读>>写)更新问题。既然读写并发操作,那么就涉及到操作互斥,这里自然想到了读写锁,本文对读写锁方面的知识做个梳理。
StampedLock是 JDK1.8 版本中在 J.U.C 并发包里新增的一个锁,StampedLock是对读写锁ReentrantReadWriteLock的增强,优化了读锁、写锁的访问,更细粒度控制并发。这篇文章就来介绍一下StampedLock,分为如下几个问题:
前面介绍了java中排它锁,共享锁的底层实现机制,本篇再进一步,学习非常有用的读写锁。鉴于读写锁比其他的锁要复杂,不想堆一大波的文字,本篇会试图图解式说明,把读写锁的机制用另外一种方式阐述,鉴于本人水平有限,如果哪里有误,请不吝赐教。
AQS通过 同步状态state来表示锁(0—锁可用,非0—锁不可用,可重入锁每重入一次state+1)
通过以下几部分来分析Java提供的读写锁ReentrantReadWriteLock:
Java 并发包中的读写锁及其实现分析 1. 前言 在Java并发包中常用的锁(如:ReentrantLock),基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时 刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得 并发性相比一般的排他锁有了很大提升。 除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化读写交互场景的编程方式。假设在程序中定义一个共享的数据结构用作缓存,它大
我们开发中应该能够遇到这样的一种情况,对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是当一个写者线程在写这些共享资源时,就不允许其他线程进行访问。
今天来分析一下读锁的获取和释放过程,读锁相比较写锁要稍微复杂一点,其中还有一点有争议的地方——锁降级。
读写锁的内部包含两把锁:一把是读(操作)锁,是一种共享锁;另一把是写(操作)锁,是一种独占锁。在没有写锁的时候,读锁可以被多个线程同时持有。写锁是具有排他性的:如果写锁被一个线程持有,其他的线程不能再持有写锁,抢占写锁会阻塞;进一步来说,如果写锁被一个线程持有,其他的线程不能再持有读锁,抢占读锁也会阻塞。
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。 针对这种场景,JAVA 的并发包提供了读写锁 ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁。 线程进入读锁的条件:
之前提到的ReentrantLock是排他锁,这种锁同一时刻只允许一个线程访问,而读写锁同一时刻可以多个线程访问,但在写线程访问时,所有读线程和其他写线程都要被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读写锁,使得并发性相比一般的排他锁有很大提升。
转自:https://www.javadoop.com/post/reentrant-read-write-lock#toc5
前文我们有介绍《看了CopyOnWriteArrayList后自己实现了一个CopyOnWriteHashMap》 关于CopyOnWrite容器的,但是它也有一些缺点:
来到多线程的第十二篇,前十一篇请点文末底部的上、下一篇标签。这篇聊聊读写锁。什么是读锁 & 写锁?开篇之前先聊聊这小两口的定义:
最近做的一个小项目中有这样的需求: 整个项目有一份config.json保存着项目的一些配置,是存储在本地文件的一个资源,并且应用中存在读写(读>>写)更新问题。既然读写并发操作,那么就涉及到操作互斥,这里自然想到了读写锁,也顺便对自己读写锁方面的知识做个梳理。
读写锁维护了一对相关的锁,一个用于只读操作,一个用于写入操作。 只要没有writer,读锁可以由多个reader线程同时保持。写锁是独占的。 互斥锁一次只允许一个线程访问共享数据,哪怕进行的是只读操作 读写锁允许对共享数据进行更高级别的并发访问 对于写操作,一次只有一个线程(write线程)可以修改共享数据 对于读操作,允许任意数量的线程同时进行读取。 与互斥锁相比,使用读写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用,即在同一时间试图对该数据执行读取或写入操作的线程数
" 在看完 ReentrantLock 之后,在高并发场景下 ReentrantLock 已经足够使用,但是因为 ReentrantLock 是独占锁,同时只有一个线程可以获取该锁,而很多应用场景都是读多写少,这时候使用 ReentrantLock 就不太合适了。读多写少的场景该如何使用?在 JUC 包下同样提供了读写锁 ReentrantReadWriteLock 来应对读多写少的场景。 "
StampedLock是Java 8引入的一种新的锁机制,它提供了乐观读锁和悲观读写锁的能力。与传统的ReentrantLock和ReentrantReadWriteLock相比,StampedLock在并发性能上有了显著的提升。这是因为它支持一种称为“乐观读”的锁策略,该策略允许多个线程同时读取共享资源,而无需阻塞或等待其他线程的锁释放。
a)Java中的锁——Lock和synchronized中介绍的ReentrantLock和synchronized基本上都是排它锁,意味着这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,在写线程访问的时候其他的读线程和写线程都会被阻塞。读写锁维护一对锁(读锁和写锁),通过锁的分离,使得并发性提高。
ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。 所有读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。 读写锁比互斥锁允许对于共享数据更大程度的并发。每次只能有一个写线程,但是同时可以有多个线程并发地读数据。ReadWriteLock适用于读多写少的并发情况。 Java并发包中ReadWriteLock是一个接口,主要有两个方法,如下:
此篇博客所有源码均来自JDK 1.8 重入锁ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少。然而读服务不存在数据竞争问题,如果一个线程在读时禁止其他线程读势必会导致性能降低。所以就提供了读写锁。 读写锁维护着一对锁,一个读锁和一个写锁。通过分离读锁和写锁,使得并发性比一般的排他锁有了较大的提升:在同一时间可以允许多个读线程同时访问,但是在写线程访问时,所有读线程和写线程都会被阻塞。 读写锁的主要特性: 公平性
本文内容:读写锁 ReentrantReadWriteLock 的源码分析,基于 Java7/Java8。
读写锁(Readers-Writer Lock)顾名思义是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。总结来说,读写锁的特点是:读读不互斥、读写互斥、写写互斥。
JUC包里面已经有一个ReentrantLock了,为何还需要一个ReentrantReadWriteLock呢?看看头注解找点线索。
Lock接口下的子类存在 ReentrantLock子类,该子类是一个线程同步处理类;ReentrantLock类的介绍详见XXX;
ReentrantReadWriteLock,即可重入的读写锁,它维护了两把锁:读锁和写锁。读锁允许多个线程同时持有,从而允许多个线程同时读取共享资源,提高了并发读取的效率。而写锁则是独占的,同一时间只能被一个线程持有,用于保护写入共享资源的操作。
面试真题,用通俗的例子解释清楚 MySQL 为什么有了表锁和行锁之后,还要引入意向锁
重入锁ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少。然而读服务不存在数据竞争问题,如果一个线程在读时禁止其他线程读势必会导致性能降低。所以就提供了读写锁。
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。
最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响。
到这其实构造方法就已经看完了,此处想表达的一点是:(ReentrantReadWriteLock里的属性sync)、(reentrantReadWriteLock.readLock的属性sync)和(reentrantReadWriteLock.writeLock的属性sync)是同一个sync(extends AbstractQueuedSynchronizer)
接下来几篇文章会对JUC并发包里面的锁工具类做下梳理,如:ReentrantLock、
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那 么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以 应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源, 就不应该允许其他线程对该资源进行读和写的操作了。
上两篇的内容中已经介绍到了锁的实现主要有ReentrantLock和ReentrantReadWriteLock。
ReadWriteLock是jdk的juc包中提供的读写锁api,维护一对关联的读锁、写锁,读锁可以被多个读线程共享,写锁排他。
读写锁区别与互斥锁的主要区别就是读锁之间是共享的,多个goroutine可以同时加读锁,但是写锁与写锁、写锁与读锁之间则是互斥的
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。
我们在上一篇写ReentrantReadWriteLock读写锁的末尾留了一个小坑,那就是读写锁因为写锁的悲观性,会导致 “写饥饿”,这样一来会大大的降低读写效率,而今天我们就来将此坑填之!填坑工具为:StampedLock,一个素有Java锁王称号的同步类,也是在 java.util.concurrent.locks 包中。
我们之前说到,ReentrantLock是独占锁,某一时刻只有一个线程可以获取该锁,而实际上会存在很多读多写少的场景,而读操作本身并不会存在数据竞争问题,如果使用独占锁,可能会导致其中一个读线程使其他的读线程陷入等待,降低性能。
StampedLock类是JDK8里面新增的一个并发工具类,这个类比较特殊,在此之前我们先简单的了解一下关于数据库或者存储系统的锁策略和机制。
(1) tryAcquireShared 小于 0 代表没有获取到共享锁 (2) doAcquireShared 将当前节点进入阻塞队列中等待被唤醒,步骤2是挂起自己 (3) 被唤醒后就可以拿到共享锁了, 步骤三 (4) 然后 setHeadAndPropagate 唤醒其他线程
前面已经讲过很多Golang系列知识,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html,
领取专属 10元无门槛券
手把手带您无忧上云