ReentrantReadWriteLock类

为了有了ReentrantLock还需要ReentrantReadWriteLock

ReentrantReadWriteLock是一个读写锁,它允许多个读操作同时进行,但在写操作时会阻止其他所有读和写操作。这种锁特别适合于读取远多于写入的场景,因为它可以提高并发性而不会牺牲数据一致性。

  1. 分离的读锁和写锁:ReentrantReadWriteLock提供了两个锁,一个用于读操作(readLock),另一个用于写操作(writeLock)。
  2. 读锁共享性:读锁可以被多个线程同时持有,只要没有线程持有写锁。
  3. 写锁独占性:写锁是独占的,只能由一个线程持有,并且在任何线程持有写锁时,读锁也不能被其他线程持有。
  4. 公平性选择:与ReentrantLock一样,ReentrantReadWriteLock也可以设置为公平模式。

尽管ReentrantLock提供了强大的功能,但它不支持区分读写操作的锁定策略。在某些情况下,使用ReentrantReadWriteLock可能更高效,因为它允许多个线程同时读取共享资源,从而减少了锁竞争的可能性。这对于读多写少的数据结构特别有用,比如缓存、计数器、集合等。

源码分析

在这里插入图片描述

ReentrantReadWriteLock实现了ReadWriteLock接口,ReadWriteLock接口定义了获取读锁和写锁的规范,具体需要实现类去实现;同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。Sync继承自AQS、NonfairSync继承自Sync类、FairSync继承自Sync类;ReadLock实现了Lock接口、WriteLock也实现了Lock接口。

内部类分析

  1. ReadLock: 实现了读锁的功能,提供了读取时的锁定和释放操作。当线程请求读锁时,如果当前没有写锁被其他线程持有,则允许获取读锁。
  2. WriteLock: 实现了写锁的功能,提供了写入时的排他锁定和释放操作。当线程请求写锁时,会阻塞直到所有读锁被释放并且没有其他写锁被持有。
  3. FairSync和NonfairSync: 都是 Sync 的子类,FairSync实现了公平的锁获取策略。在公平模式下,等待时间最长的线程将获得锁。NonfairSync实现了非公平的锁获取策略。在非公平模式下,不保证按照请求锁的顺序授予锁,新来的线程可能会插队获取锁。
  4. RWLock: 定义了读写锁的基本操作。
  5. SyncHelper: 用于帮助管理同步状态。
  6. HoldCounter: HoldCounter主要与读锁配套使用。HoldCounter主要有两个属性,count和tid,其中count表示某个读线程重入的次数,tid表示该线程的tid字段的值,该字段可以用来唯一标识一个线程

主要方法

  • tryRelease 该方法用于释放写锁资源,首先会判断该线程是否为独占线程,若不为独占线程,则抛出异常,否则,计算释放资源后的写锁的数量,若为0,表示成功释放,资源不将被占用,否则,表示资源还被占用。
protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }
  • tryAcquire 用于实现锁的获取逻辑,允许线程以可重入的方式获取锁。其中,变量c为锁状态,变量w表示当前状态c中独占锁的持有数量。如果当前状态不为0,说明锁已经被持有。如果当前锁是共享的(w == 0),或者持有锁的线程不是当前线程,则直接返回false,表示获取失败。前状态不为0,说明锁已经被持有。如果当前线程已经持有锁(判断了重入次数是否超过了最大限制),则简单增加状态计数并返回true,表示获取成功:
  boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
                //设置当前线程为独占锁的所有者
            setExclusiveOwnerThread(current);
            return true;
        }
  • tryReleaseShared 这个方法的作用是尝试释放共享锁(读锁)。
 protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
           // 当前线程为第一个读线程
            if (firstReader == current) {
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                //获取缓存的HoldCounter对象rh,如果rh为空或者rh.tid不等于当前线程的ID,则从readHolds中获取新的HoldCounter对象。
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                //从readHolds中移除当前的HoldCounter对象。
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                //尝试更新状态值
                if (compareAndSetState(c, nextc))
                   
                    return nextc == 0;
            }
        }
  • tryAcquireShared 用于读锁线程获取读锁。如果exclusiveCount©不等于0且getExclusiveOwnerThread()不等于当前线程,说明有其他线程持有写锁,此时无法获取共享锁,直接返回-1。如果readerShouldBlock()为false且r(读取次数)小于MAX_COUNT,尝试通过CAS操作更新状态值c,将共享锁计数器加1。如果更新成功,根据不同情况更新firstReader和firstReaderHoldCount的值,并返回1表示获取共享锁成功。
 protected final int tryAcquireShared(int unused) {
           
            Thread current = Thread.currentThread();
            int c = getState();
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            int r = sharedCount(c);
            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);
        }

代码使用示例

ReadThread和WriteThread是自定义的线程类,它们分别代表读线程和写线程。读线程:

public class ReadThread extends Thread{

    private ReentrantReadWriteLock rrwLock;

    public ReadThread(String name, ReentrantReadWriteLock rrwLock) {
        super(name);
        this.rrwLock = rrwLock;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " trying to lock");
        try {
            rrwLock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + " lock successfully");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rrwLock.readLock().unlock();
            System.out.println(Thread.currentThread().getName() + " unlock successfully");
        }
    }
}

写线程:

public class WriteThread extends Thread{

    private ReentrantReadWriteLock rrwLock;

    public WriteThread(String name, ReentrantReadWriteLock rrwLock) {
        super(name);
        this.rrwLock = rrwLock;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " trying to lock");
        try {
            rrwLock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + " lock successfully");
        } finally {
            rrwLock.writeLock().unlock();
            System.out.println(Thread.currentThread().getName() + " unlock successfully");
        }
    }
}

使用,创建两个读线程和一个写线程。然后启动这些线程:


        public static void main(String[] args) {
            ReentrantReadWriteLock rrwLock = new ReentrantReadWriteLock();
            ReadThread rt1 = new ReadThread("rt1", rrwLock);
            ReadThread rt2 = new ReadThread("rt2", rrwLock);
            WriteThread wt1 = new WriteThread("wt1", rrwLock);
            rt1.start();
            rt2.start();
            wt1.start();
        }

执行结果:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为OD机试 - 小扇和小船的数字游戏 - 二进制(Java 2024 C卷 200分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

VsCode | 修改首页启动页 Logo

VsCode | 修改首页启动页 Logo 最终效果&#xff1a; 插件的安装 先安装插件 Custom CSS and JS Loader 插件配置 Ctrl Shift P 输入 打开用户设置&#xff0c;在末尾添加 "vscode_custom_css.imports": [""]下载 Logo 下载 Logo 点我下载 引入…

SDB2F3 1.5A,高达28V输出1.2MHz升压转换器芯片IC

一般说明 该SDB2F3是一个恒定的频率&#xff0c;5针SOT23用于小型低功率应用的电流模式升压转换器。 该SDB2F3开关在1.2MHz&#xff0c;并允许使用微小&#xff0c;低成本的电容器和电感2毫米或更少的高度。内部软启动的结果在小浪涌电流和延长电池寿命。 该SDB2F3工作从…

string底层浅析

char简单易用,但是string是万金油 char *b "123"; string a{"123"};a是不是地址 发现a是地址 a的地址是不是和a[0]地址重合 #include<iostream> #include<cstring> using namespace std; int main() {string a{ "123" };char g[…

Pytorch分布式train——pytorch.distributed.launch V.S. torchrun

1. 较早的pytorch.distributed.launch python -m torch.distributed.launch --nproc_per_node4 --nnodes1 --node_rank0 train.py --args XXX 参数解析&#xff1a; nnodes&#xff1a;节点&#xff08;主机&#xff09;的数量&#xff0c;通常一个节点对应一个主机 node_rank…

探索动态内存开辟的奥秘

✨✨欢迎&#x1f44d;&#x1f44d;点赞☕️☕️收藏✍✍评论 个人主页&#xff1a;秋邱博客 所属栏目&#xff1a;C语言 前言 开始之前&#xff0c;我们先来了解一下C/C中程序内存区域划分。 在C/C程序中&#xff0c;内存区域通常被划分为以下几个部分&#xff1a; 1.栈&…

漏洞挖掘之某厂商OAuth2.0认证缺陷

0x00 前言 文章中的项目地址统一修改为: a.test.com 保护厂商也保护自己 0x01 OAuth2.0 经常出现的地方 1&#xff1a;网站登录处 2&#xff1a;社交帐号绑定处 0x02 某厂商绑定微博请求包 0x02.1 请求包1&#xff1a; Request: GET https://www.a.test.com/users/auth/weibo?…

C++设计模式-创建型设计模式

设计模式 设计模式是什么 设计模式是指在软件开发中&#xff0c;经过验证的&#xff0c;用于解决在特定环境下&#xff0c;重复出现的&#xff0c;特定问题的解决方案&#xff1b;其实就是解决问题的固定套路。但是要慎用设计模式&#xff0c;有一定的工程代码量之后用它比较…

Hdfs小文件治理策略以及治理经验

小文件是 Hadoop 集群运维中的常见挑战&#xff0c;尤其对于大规模运行的集群来说可谓至关重要。如果处理不好&#xff0c;可能会导致许多并发症。Hadoop集群本质是为了TB,PB规模的数据存储和计算因运而生的。为啥大数据开发都说小文件的治理重要&#xff0c;说HDFS 存储小文件…

Python字符串常用方法(全网最细,仅此一份)

🥇作者简介:CSDN内容合伙人、新星计划第三季Python赛道Top1 🔥本文已收录于Python系列专栏: 👉Python从入门到精通 💬订阅专栏后可私信博主进入Python学习交流群,进群可领取Python180G全栈视频教程以及Python相关电子书合集 😊私信未回可以加V:hacker0327 备注P…

Word文件后缀

Word文件后缀 .docx文件为Microsoft Word文档后缀名&#xff0c;基于XML文件格式 .dotm为Word启用了宏的模板 .dotx为Word模板 .doc为Word97-2003文档&#xff0c;二进制文件格式 参考链接 Word、Excel 和 PowerPoint 的文件格式参考 Learn Microsoft

u盘格式化后电脑读不出来怎么办?u盘格式化的东西还能恢复吗

随着科技的快速发展&#xff0c;U盘已成为我们日常生活和工作中不可或缺的数据存储工具。然而&#xff0c;有时我们可能会遇到U盘格式化后电脑无法读取的情况&#xff0c;或是误格式化导致重要数据丢失。面对这些问题&#xff0c;我们该如何应对&#xff1f;本文将为您详细解答…

C语言 main( ) 函数的指针数组形参是怎么回事?

一、问题 在使⽤⼀些开发⼯具⽣成C语⾔⽂件时&#xff0c;主函数 mian( ) 中会有参数&#xff0c;这个参数到底是怎么回事⼉呢&#xff1f; 二、解答 mian( ) 称为主函数&#xff0c;是所有程序运⾏的⼊口。 mian( ) 函数是由系统调⽤的&#xff0c;当处于操作命令状态下&…

解锁学术语言:掌握论文释义工具的高效使用技巧

研究论文是一份书面文件&#xff0c;其中包括对特定主题的论点、想法和观点的概述。释义至关重要&#xff0c;因为它可以为您的工作增添意义和价值。教育释义的核心目的是增加你的写作的价值&#xff0c;同时考虑其他作家的观点和发现&#xff0c;并建立与你的主题的相关性。通…

恶补《操作系统》5_1——王道学习笔记

5设备管理 5.1_1 I-O设备的概念和分类 1、什么是I-O设备 输入/输出&#xff1a;I/O设备就是可以将数据输入到计算机&#xff0c;或者可以接收计算机输出数据的外部设备&#xff0c;属于计算机中的硬件部件。 2、按使用特性分类 人机交互的外部设备存储设备网络通信设备 3、…

k8s Dashboard 运维维护记录

k8s Dashboard 运维维护记录 k8s Dashboard 运维维护记录 Q1&#xff1a;需要使用firefox浏览器访问 提示了证书错误NET::ERR_CERT_INVALID&#xff0c;原因是由于物理机的浏览器证书不可用 需要注意的是&#xff0c;若提示“连接不安全”的警告时&#xff0c;点击“高级”…

计算机的翻译(编译和链接)过程

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C语言基本概念 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 &#x1f697;1.翻译环境和运行环境&#xff1…

AI大模型探索之路-训练篇12:语言模型Transformer库-Datasets组件实践

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

深入理解 LinkedList 及底层源码分析

LinkedList 是基于链表结构的一种 List&#xff0c;在分析 LinkedList 源码前我们先对对链表结构做一个简单的了解。 一、链表的概念 链表是由一系列非连续的节点组成的存储结构&#xff0c;简单分下类的话&#xff0c;链表又分为_单向链表和双向链表&#xff0c;而单向 / 双…

函数之对决!!:数学函数 VS C++函数

前言 有人问过我&#xff0c;C里的函数是否跟我们数学里的函数一样&#xff1f;于是&#xff0c;我就写下了这篇文章。 一、数学函数 1、一次函数 一次函数&#xff0c;也称为线性函数&#xff0c;是数学中函数的一种&#xff0c;通常表示为ykxb&#xff08;其中k和b是常数&am…