java多线程(二五)ReentrantReadWriteLock读写锁详解(1)

news2025/1/11 0:16:04

一、读写锁简介

现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。

针对这种场景,JAVA的并发包提供了读写锁ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁,描述如下:

线程进入读锁的前提条件:

没有其他线程的写锁,

没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。

线程进入写锁的前提条件:

没有其他线程的读锁

没有其他线程的写锁

而读写锁有以下三个重要的特性:

(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。

(2)重进入:读锁和写锁都支持线程重进入。

(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。

二、源码解读

我们先来看下 ReentrantReadWriteLock 类的整体结构:

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {

    /** 读锁 */
    private final ReentrantReadWriteLock.ReadLock readerLock;

    /** 写锁 */
    private final ReentrantReadWriteLock.WriteLock writerLock;

    final Sync sync;
    
    /** 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock */
    public ReentrantReadWriteLock() {
        this(false);
    }

    /** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    /** 返回用于写入操作的锁 */
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    
    /** 返回用于读取操作的锁 */
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }


    abstract static class Sync extends AbstractQueuedSynchronizer {}

    static final class NonfairSync extends Sync {}

    static final class FairSync extends Sync {}

    public static class ReadLock implements Lock, java.io.Serializable {}

    public static class WriteLock implements Lock, java.io.Serializable {}
}

1、类的继承关系

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {}

说明:可以看到,ReentrantReadWriteLock实现了ReadWriteLock接口,ReadWriteLock接口定义了获取读锁和写锁的规范,具体需要实现类去实现;同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。

2、类的内部类

ReentrantReadWriteLock有五个内部类,五个内部类之间也是相互关联的。内部类的关系如下图所示。
在这里插入图片描述
说明:如上图所示,Sync继承自AQS、NonfairSync继承自Sync类、FairSync继承自Sync类(通过构造函数传入的布尔值决定要构造哪一种Sync实例);ReadLock实现了Lock接口、WriteLock也实现了Lock接口。

3.Sync类:

(1)类的继承关系

abstract static class Sync extends AbstractQueuedSynchronizer {}

说明:Sync抽象类继承自AQS抽象类,Sync类提供了对ReentrantReadWriteLock的支持。

(2)类的内部类

Sync类内部存在两个内部类,分别为HoldCounter和ThreadLocalHoldCounter,其中HoldCounter主要与读锁配套使用,其中,HoldCounter源码如下。

// 计数器
static final class HoldCounter {
    // 计数
    int count = 0;
    // Use id, not reference, to avoid garbage retention
    // 获取当前线程的TID属性的值
    final long tid = getThreadId(Thread.currentThread());
}

说明:HoldCounter主要有两个属性,count和tid,其中count表示某个读线程重入的次数,tid表示该线程的tid字段的值,该字段可以用来唯一标识一个线程。ThreadLocalHoldCounter的源码如下

// 本地线程计数器
static final class ThreadLocalHoldCounter
    extends ThreadLocal<HoldCounter> {
    // 重写初始化方法,在没有进行set的情况下,获取的都是该HoldCounter值
    public HoldCounter initialValue() {
        return new HoldCounter();
    }
}

说明:ThreadLocalHoldCounter重写了ThreadLocal的initialValue方法,ThreadLocal类可以将线程与对象相关联。在没有进行set的情况下,get到的均是initialValue方法里面生成的那个HolderCounter对象。

(3)类的属性

abstract static class Sync extends AbstractQueuedSynchronizer {
    // 版本序列号
    private static final long serialVersionUID = 6317671515068378041L;        
    // 高16位为读锁,低16位为写锁
    static final int SHARED_SHIFT   = 16;
    // 读锁单位
    static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
    // 读锁最大数量
    static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
    // 写锁最大数量
    static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
    // 本地线程计数器
    private transient ThreadLocalHoldCounter readHolds;
    // 缓存的计数器
    private transient HoldCounter cachedHoldCounter;
    // 第一个读线程
    private transient Thread firstReader = null;
    // 第一个读线程的计数
    private transient int firstReaderHoldCount;
}

说明:该属性中包括了读锁、写锁线程的最大量。本地线程计数器等。

(4)类的构造函数

// 构造函数
Sync() {
    // 本地线程计数器
    readHolds = new ThreadLocalHoldCounter();
    // 设置AQS的状态
    setState(getState()); // ensures visibility of readHolds
}

说明:在Sync的构造函数中设置了本地线程计数器和AQS的状态state。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有关3dmax对齐技巧的那些事

建模操作中&#xff0c;对齐是非常常用的一个功能&#xff0c;用好这个对齐功能能够事半功倍&#xff0c;好处我不说了&#xff0c;下面我们这篇博文就来说说3dmax对齐技巧的相关的内容。 文章目录一、点对齐1、样条线中的点对齐2、多边形中的点对齐二、线对齐三、面对齐四、物…

DJI ROS dji_sdk 源码分析|整体框架

DJI ROS dji_sdk 源码分析|整体框架launch文件CMakeLists.txtcpp文件main.cppOSDK 是一个用于开发无人机应用程序的开发工具包&#xff0c;基于OSDK 开发的应用程序能够运行在机载计算机上&#xff08;如Manifold 2&#xff09;&#xff0c;开发者通过调用OSDK 中指定的接口能够…

计算机网络考研-第一章学

计算机网学习总结第一章计算机系统概述1.1.1 导学1.1.2 操作系统的特征1.2 操作系统的发展与分类1.3 操作系统的运行环境1.3.1 操作系统的运行机制1.3.2 中断和异常1.3.3系统调用&#xff1a;1.3.4 操作系统的体系结构第一章总结第一章计算机系统概述 1.1.1 导学 1.1.2 操作系…

Nginx 配置实例-反向代理案例一

实现效果&#xff1a;使用nginx反向代理&#xff0c;访问 www.suke.com 直接跳转到本机地址127.0.0.1:8080 一、准备工作 Centos7 安装 Nginxhttps://liush.blog.csdn.net/article/details/125027693 1. 启动一个 tomcat Centos7安装JDK1.8https://liush.blog.csdn.net/arti…

简单粗暴的分布式定时任务解决方案

分布式定时任务1.为什么需要定时任务&#xff1f;2.数据库实现分布式定时任务3.基于redis实现1.为什么需要定时任务&#xff1f; 因为有时候我们需要定时的执行一些操作&#xff0c;比如业务中产生的一些临时文件&#xff0c;临时文件不能立即删除&#xff0c;因为不清楚用户是…

基于FPGA实现正弦插值算法

1、正弦插值的算法分析 1.1 信号在时域与频域的映射关系 在进行正弦算法分析之前&#xff0c;我们回顾一下《数字信号处理》课程中&#xff0c;对于信号在时域与频域之间的映射关系&#xff0c;如下图。 对于上图中的原始信号x(t)&#xff0c;使用ADC对信号进行采样&#xff0…

【操作系统】进程句柄

进程句柄句柄是什么为什么需要句柄作用句柄是什么 先给结论&#xff0c;句柄&#xff08;handle&#xff09;实际上是一个指向指针的指针。 它指向进程所要访问的进程对象的地址&#xff0c;是用来找到目标进程的索引&#xff0c;当我们想要访问对象进程时&#xff0c;就要利…

从一道面试题看 TCP 的吞吐极限

分享一个 TCP 面试题&#xff1a;单条 TCP 流如何打满香港到旧金山的 320Gbps 专线&#xff1f;(补充&#xff0c;写成 400Gbps 更具迷惑性&#xff0c;但预测大多数人都会跑偏&#xff0c;320Gbps 也就白给了) 这个题目是上周帮一个朋友想的&#xff0c;建议他别问三次握手&a…

C#:Krypton控件使用方法详解(第十六讲) ——kryptonCheckedListBox

今天介绍的Krypton控件中的kryptonCheckedListBox。下面介绍控件的外观属性如下图所示&#xff1a;Cursor属性&#xff1a;表示鼠标移动过该控件的时候&#xff0c;鼠标显示的形状。属性值如下图所示&#xff1a;UseWaitCursor属性&#xff1a;表示鼠标在控件中等待时&#xff…

问ChatGPT:零基础如何学好.Net Core?

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 ChatGPT横空出世&#xff0c;一下子让全球互联网企业都慌了&#xff0c;纷纷表示&#xff1a;马上跟进发布ChatGPT&#xff0c;媒体纷纷报道大有改变教培行业。 下面我们问问ChatGPT&#xff1a;零基础如何学好…

EasyCVR视频融合平台开放插件功能:支持EasyNTS与EasyShark抓包

EasyCVR视频融合平台基于云边端一体化架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;平台支持海量视频汇聚管理&#xff0c;能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;实现视频资源的鉴权管理、按需调阅…

GPS/GPRS车载定位系统智能终端设计μC/OS-Ⅱ调度液晶显示汽车行驶记录仪电路

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;gps电路 免费下载完整无水印论文报告&#xff08;包含主板电路图和采集板电路图&#xff09; 文章目录一、绪论二、车载智能终端三、车载智能终端的硬件结构及设计四、车载智能终端的软件结构及设计五、总结和展望附录一 汽…

小i机器人登陆纳斯达克:市值4.2亿美元,与苹果打了10年专利侵权官司

‍数据智能产业创新服务媒体——聚焦数智 改变商业要问当前科技圈里最靓的仔是谁&#xff1f;那当然是非 ChatGPT莫属。当下谁能推出真正意义上的中国版ChatGPT&#xff0c;并且在这轮AI浪潮竞争白热化阶段中笑到最后&#xff0c;已经成为人们关注的焦点。美东时间3月9日&…

使用chatgpt写6.5分作文范文

其实使用chatgpt最大的背单词好处就是你可以看到真正的外国人的思维到底是如何的。而且&#xff0c;你也可以看到chatgpt这个模型&#xff0c;如果是编写代码的话&#xff0c;你如果使用中文&#xff0c;它编写的效果是没有英文输入的好的&#xff0c;为什么呢&#xff1f;因为…

Vector - CAPL - log回放模块函数

Replay log回放模块作为我们常见的问题分析小工具,是大部分车载圈老人的必备工具,不过自从CANoe软件11.0之后的版本变动依然无法阻挡每一个车载人使用的热情,除了我们常见的手动回放log外,我们工作中还有一部分低概率以及极低概率出现的问题,这时候我们就需要通过自动化对…

python趣味编程-2048游戏

在上一期我们用Python实现了一个盒子追逐者的游戏&#xff0c;这一期我们继续使用Python实现一个简单的2048游戏&#xff0c;让我们开始今天的旅程吧~ 在 Python 免费源代码中使用 Tkinter 的简单 2048 游戏 使用 Tkinter 的简单 2048 游戏是一个用Python编程语言编码的桌面游…

阅读笔记DeepAR: Probabilistic Forecasting with Autoregressive Recurrent Networks

zi,t∈Rz_{i,t}\in \mathbb{R}zi,t​∈R表示时间序列iii在ttt时刻的值。给一个连续时间段t∈[1,T]t\in [1, T]t∈[1,T]&#xff0c;将其划分为context window[1,t0)[1,t_0)[1,t0​)和prediction window[t0,T][t_0,T][t0​,T]。用context window的时间序列预测prediction window…

关于异常控制流和系统级 I/O:进程

&#x1f4ad; 写在前面&#xff1a;本文将学习《深入理解计算机系统》的第六章 - 关于异常控制流和系统级 I/O 的 进程部分。CSAPP 是计算机科学经典教材《Computer Systems: A Programmers Perspective》的缩写&#xff0c;该教材由Randal E. Bryant和David R. OHallaron 合著…

C 去除字符串中重复字母(LeetCode)

&#x1f31f;前言摆烂太久&#xff0c;好久没有更文了&#xff0c;小九和大家一起看看题写写题找回手感吧&#xff0c;也希望这篇文章可以帮助正在寻找解题答案的朋友&#xff0c;你们的支持就是我最大的动力&#xff01;求三连&#xff01;求关注呀&#xff01;&#x1f31f;…

信息系统项目管理师第4版教材的变化:PMBOK第六版和第七版的叠加

昨天下午&#xff0c;软考官方网站突然发布了第4版考纲和教材&#xff0c;这让很多在准备今年上半年信息系统项目管理师考试的考生们有些紧张。那么这次教材改版主要的变化是什么&#xff0c;5月份的考试是否会按照新版的教材来考呢&#xff1f;让我们逐个章节进行分析。信息化…