AQS之ReentrantReadWriteLock

2023-12-28 11:28

本文主要是介绍AQS之ReentrantReadWriteLock,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

AQS之ReentrantReadWriteLock

一. 归纳总结

  1. ReentrantReadWriteLock适合读多写少的场景。是可重入的读写锁实现类。其中, 写锁是独占的,读锁是共享的。

  2. 支持锁降级(持有写锁、获取读锁,最后释放写锁的过程)

  • 锁降级可以帮助我们拿到当前线程修改后的结果而不被其他线程所破坏,防止更新丢失。
  • 可以保证数据的可见性,如果当前线程不获取读锁而是直接释放写锁,假设此刻另一个线程(记作线程T)获取了写锁并修改了数据,那么当前线程无法感知线程T的数据更新。如果当前线程获取读锁,即遵循锁降级的步骤,则线程T将会被阻塞,直到当前线程使用数据并释放读锁之后,线程T才能获取写锁进行数据更新。
  1. 不支持锁升级(持有读锁、获取写锁,最后释放读锁的过程)
    目的也是保证数据可见性,如果读锁已被多个线程获取,其中任意线程成功获取了写锁并更新了数据,则其更新对其他获取到读锁的线程是不可见的。

二.读写状态的设计

读写锁对于同步状态的实现是在一个整形变量上通过“按位切割使用”:将变量切割成两部分,高16位表示读,低16位表示写。
在这里插入图片描述
假设当前同步状态值为S,get和set的操作如下:

1、获取写状态:

S&0x0000FFFF:将高16位全部抹去

2、获取读状态:

S>>>16:无符号补0,右移16位

3、写状态加1:

 S+1

4、读状态加1:

S+(1<<16)即S + 0x00010000

在代码层面的判断中,同步状态S不等于0时,当写状态(S&0x0000FFFF)等于0时,则读状态(S>>>16)大于0,即读锁已被获取。

三.写锁的获取与释放

3.1 写锁的获取

  • 写锁是一个支持重进入的排它锁。如果当前线程已经获取了写锁,则增加写状态。如果当前线程在获取写锁时,读锁已经被获取(读状态不为0)或者该线程不是已经获取写锁的线程, 则当前线程进入等待状态。
  • 写锁的获取是通过重写AQS中的tryAcquire方法实现的。
protected final boolean tryAcquire(int acquires) {//当前线程Thread current = Thread.currentThread();//获取state状态   存在读锁或者写锁,状态就不为0int c = getState();//获取写锁的重入数int w = exclusiveCount(c);//当前同步状态state != 0,说明已经有其他线程获取了读锁或写锁if (c != 0) {// c!=0 && w==0 表示存在读锁// 当前存在读锁或者写锁已经被其他写线程获取,则写锁获取失败if (w == 0 || current != getExclusiveOwnerThread())return false;// 超出最大范围  65535if (w + exclusiveCount(acquires) > MAX_COUNT)throw new Error("Maximum lock count exceeded");//同步state状态setState(c + acquires);return true;}// writerShouldBlock有公平与非公平的实现, 非公平返回false,会尝试通过cas加锁//c==0 写锁未被任何线程获取,当前线程是否阻塞或者cas尝试获取锁 if (writerShouldBlock() ||!compareAndSetState(c, c + acquires))return false;//设置写锁为当前线程所有setExclusiveOwnerThread(current);return true;

源码分析:

  • 读写互斥
  • 写写互斥
  • 写锁支持同一个线程重入
  • writerShouldBlock写锁是否阻塞实现取决公平与非公平的策略(FairSync和NonfairSync)
    在这里插入图片描述

3.2 读锁的获取

实现共享式同步组件的同步语义需要通过重写AQS的tryAcquireShared方法和tryReleaseShared方法。读锁的获取实现方法为:

protected final int tryAcquireShared(int unused) {Thread current = Thread.currentThread();int c = getState();// 如果写锁已经被获取并且获取写锁的线程不是当前线程,当前线程获取读锁失败返回-1   判断锁降级if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)return -1;//计算出读锁的数量int r = sharedCount(c);/*** 读锁是否阻塞    readerShouldBlock()公平与非公平的实现* r < MAX_COUNT: 持有读锁的线程小于最大数(65535)*  compareAndSetState(c, c + SHARED_UNIT) cas设置获取读锁线程的数量*/if (!readerShouldBlock() &&r < MAX_COUNT &&compareAndSetState(c, c + SHARED_UNIT)) {   //当前线程获取读锁if (r == 0) {  //设置第一个获取读锁的线程firstReader = current; firstReaderHoldCount = 1;  //设置第一个获取读锁线程的重入数} else if (firstReader == current) { // 表示第一个获取读锁的线程重入firstReaderHoldCount++;} else { // 非第一个获取读锁的线程HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current))cachedHoldCounter = rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;  //记录其他获取读锁的线程的重入次数}return 1;}// 尝试通过自旋的方式获取读锁,实现了重入逻辑return fullTryAcquireShared(current);

源码分析:

  • 读锁共享,读读不互斥
  • 读锁可重入,每个获取读锁的线程都会记录对应的重入数
  • 读写互斥,锁降级场景除外
  • 支持锁降级,持有写锁的线程,可以获取读锁,但是后续要记得把读锁和写锁读释放
  • readerShouldBlock读锁是否阻塞实现取决公平与非公平的策略(FairSync和NonfairSync)
    在这里插入图片描述

这篇关于AQS之ReentrantReadWriteLock的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/545792

相关文章

【编程底层思考】详解Java的JUC多线程并发编程底层组件AQS的作用及原理

Java中的AbstractQueuedSynchronizer(简称AQS)是位于java.util.concurrent.locks包中的一个核心组件,用于构建锁和其他同步器。AQS为实现依赖于FIFO(先进先出)等待队列的阻塞锁和相关同步器提供了一套高效、可扩展的框架。 一、AQS的作用 统一同步状态管理:AQS提供了一个int类型的成员变量state,用于表示同步状态。子类可以根据自己

深入理解java并发编程之aqs框架

跟synchronized 相比较,可重入锁ReentrankLock其实原理有什么不同?   所得基本原理是为了达到一个目的;就是让所有线程都能看到某种标记。synchronized通过在对象头中设置标记实现了这一目的,是一种JVM原生的锁实现方式。而ReentrantLock以及所有的基于Lock接口的实现类,都是通过一个volitile修饰的int型变量,并保证每个线程都能拥有对该in

Java并发之AQS与自旋锁(利用CAS实现)

一、概述   谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)!   类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch...。   以下是本文的目录大

Java源码学习之高并发编程基础——AQS源码剖析之线程间通信之条件等待队列

1.前言&目录 前言: 在Java中,使用synchronized关键字构建的锁,线程间通信可以使用某对象实例的wait/notify机制完成。AQS同样也提供了一套线程间通信的解决方案——条件等待队列。 在AQS源码分析的两篇文章AQS源码分析(上)、AQS源码分析(下)中,我们知道了,无论是独占锁模式还是共享锁模式,AQS提供的能力是将获取不到锁的线程将它们封装成链表节点的形式组织

AQS源码一窥-JUC系列

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 AQS源码一窥 考虑到AQS的代码量较大,涉及信息量也较多,计划是先使用较常用的ReentrantLock使用代码对AQS源码进行一个分析,一窥内部实现,然

9、深入理解AQS之闭锁CountDownLatch

深入理解AQS之闭锁CountDownLatch CountDownLatch介绍CountDownLatch源码分析构造函数Sync-队列同步器await()-阻塞等待countDown()-释放锁资源总结 CountDownLatch介绍   CountDownLatch让一个或多个线程等待其他线程执行完成后再执行。在创建CountDownLatch对象时,必须指定线程数c

7、深入理解AQS之独占锁ReentrantLock

深入理解AQS之独占锁ReentrantLock 管程 - Java同步的设计思想MESA模型 AQS原理分析什么是AQSAQS核心结构AQS定义两种队列同步等待队列条件等待队列 基于AQS实现一把独占锁 ReentrantLock源码分析ReentrantLock原理lock()流程图unlock()流程图 ReentrantLock源码分析构造函数lock()加锁公平锁非公平锁acqui

JAVA - AQS源码解读

JAVA - AQS源码解读 前言 AQS全称AbstractQueuedSynchronizer。是Lock、CountDownLatch、Semaphore(信号量)的基础。这个AQS我在大约一年多以前看过一次,但是当时水平有限,根本没看懂什么意思,最后对于AQS的理解都是来自于网上的博文,可以说是吃人家嚼过的。。这次重读源码还是收获颇丰。 Node 看一下Node的结构:   1

深入理解AQS:Java并发编程中的核心组件

目录 AQS简介AQS的设计思路AQS的核心组成部分 状态(State)同步队列(Sync Queue)条件队列(Condition Queue) AQS的内部实现 节点(Node)锁的获取与释放 独占锁共享锁 条件变量 AQS的应用案例 ReentrantLockCountDownLatchSemaphore 总结参考文献 AQS简介 AbstractQueuedSynchronizer(

ReentrantLock与AQS:深入剖析多线程同步的艺术

1. 概述 ReentrantLock作为Java的独享锁,其实现基于AbstractQueuedSynchronizer(AQS)。AQS为构建锁和同步器提供了一个框架,包括资源的获取、释放、线程的排队等待等机制。 2. 入队与出队 在AQS中,等待获取锁的线程会被封装成Node节点,并加入到一个FIFO的队列中。当线程尝试获取锁失败时,会执行入队操作。入队操作主要包括将节点添加到队列