ReentrantReadWriteLock读写分离与锁降级实践

Prompt Learning 简介 • Prompt Learning 可以将所有的任务归一化预训练语言模型的任务• 避免了预训练和fine-tuning 之间的gap,几乎所有 NLP 任务都可以直接使用,不需要训练数据。• 在少样本的数据集上,能取得超过fine-tuning的效果。• 使得所有的任务在方法上变得一致。 阅读详情

一、读写锁的核心价值

在多线程编程中,同步机制是保证线程安全的关键。传统的互斥锁(如synchronized)在读多写少的场景下存在明显性能瓶颈:读操作被不必要的串行化,即使多个线程只读取数据也会相互阻塞。这正是ReentrantReadWriteLock的用武之地!

读写锁的优势

  1. 读读并发:多个线程可以同时获取读锁
  2. 读写互斥:写锁独占时阻塞所有读写操作
  3. 写写互斥:同一时刻只允许一个写操作
  4. 锁降级:写锁可安全降级为读锁(本文重点)

二、ReentrantReadWriteLock实现原理

2.1 状态分离设计

ReentrantReadWriteLock通过AQS(AbstractQueuedSynchronizer)实现,其核心在于将32位state分为两部分:

// 状态位拆分示意
static final int SHARED_SHIFT   = 16;       // 共享锁移位值
static final int EXCLUSIVE_MASK = (1 << 16) - 1; // 独占锁掩码

// 获取读锁数量(高16位)
static int sharedCount(int c) { 
    return c >>> SHARED_SHIFT; 
}

// 获取写锁重入次数(低16位)
static int exclusiveCount(int c) { 
    return c & EXCLUSIVE_MASK; 
}

2.2 锁获取规则

锁类型获取条件
读锁无写锁持有,或持有写锁的是当前线程(锁降级情况)
写锁无任何读锁且无其他线程持有写锁(可重入)

2.3 工作流程对比

读锁获取流程:

1. 检查是否有写锁持有
   ├─ 无:增加读锁计数,获取成功
   └─ 有:检查是否当前线程持有
        ├─ 是:获取成功(锁降级情况)
        └─ 否:进入等待队列

写锁获取流程:

1. 检查是否有任何锁
   ├─ 无:设置写锁状态,获取成功
   └─ 有:检查是否当前线程重入
        ├─ 是:增加写锁计数
        └─ 否:进入等待队列

三、锁降级:原理与必要性

3.1 什么是锁降级?

锁降级(Lock Downgrading) 是指线程在持有写锁的情况下:

  1. 获取读锁
  2. 释放写锁
  3. 在仅持有读锁的状态下继续操作
// 标准锁降级流程
writeLock.lock();          // 1.获取写锁
try {
    // 修改数据...
    readLock.lock();       // 2.获取读锁(关键步骤)
} finally {
    writeLock.unlock();    // 3.释放写锁(完成降级)
}

try {
    // 读取数据(受读锁保护)
} finally {
    readLock.unlock();     // 4.释放读锁
}

3.2 为什么需要锁降级?

考虑以下无锁降级的危险场景:

时间线:
1. 线程A获取写锁
2. 线程A修改数据
3. 线程A释放写锁
4. [危险间隙开始]
5. 线程B获取写锁
6. 线程B修改数据
7. 线程B释放写锁
8. [危险间隙结束]
9. 线程A获取读锁
10. 线程A读取到线程B修改的数据(非预期!)

锁降级通过在释放写锁前获取读锁,消除了这个危险间隙:

时间线:
1. 线程A获取写锁
2. 线程A修改数据
3. 线程A获取读锁
4. 线程A释放写锁
5. [读锁保护中]
6. 线程B尝试获取写锁(阻塞)
7. 线程A安全读取数据
8. 线程A释放读锁
9. 线程B获取写锁

3.3 锁降级的核心价值

  1. 数据一致性:确保线程看到自己修改的最新数据
  2. 写后读原子性:消除写锁释放到读锁获取之间的危险窗口
  3. 并发性优化:允许其他读线程并发访问最新数据

四、完整代码示例

4.1 基础读写锁使用

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
    private int sharedData = 0;

    // 写操作
    public void writeData(int value) {
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 开始写入: " + value);
            sharedData = value;
            Thread.sleep(100); // 模拟写耗时
            System.out.println(Thread.currentThread().getName() + " 写入完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            writeLock.unlock();
        }
    }

    // 读操作
    public void readData() {
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 开始读取");
            Thread.sleep(50); // 模拟读耗时
            System.out.println(Thread.currentThread().getName() + " 读取到: " + sharedData);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            readLock.unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();
        
        // 创建读线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                while (true) {
                    demo.readData();
                    sleep(200);
                }
            }, "Reader-" + i).start();
        }
        
        // 创建写线程
        for (int i = 0; i < 2; i++) {
            int id = i;
            new Thread(() -> {
                int value = 0;
                while (true) {
                    demo.writeData(value++);
                    sleep(300);
                }
            }, "Writer-" + id).start();
        }
    }
    
    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

执行效果说明:

Reader-0 开始读取
Reader-1 开始读取   // 多个读线程可以并发
Reader-0 读取到: 0
Reader-1 读取到: 0
Writer-0 开始写入: 0  // 写操作独占
Writer-0 写入完成
Reader-2 开始读取
Reader-3 开始读取   // 写完成后读操作恢复并发
Reader-2 读取到: 0
Reader-3 读取到: 0

4.2 锁降级实战

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockDowngradeDemo {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
    private volatile boolean dataValid = false;
    private int criticalData = 0;

    public void processWithDowngrade() {
        // 1. 获取写锁
        writeLock.lock();
        try {
            // 2. 准备数据(写操作)
            System.out.println("[" + Thread.currentThread().getName() + "] 获取写锁,准备数据...");
            prepareData();
            
            // 3. 获取读锁(开始降级)
            readLock.lock();
            System.out.println("[" + Thread.currentThread().getName() + "] 获取读锁(准备降级)");
        } finally {
            // 4. 释放写锁(保留读锁)
            writeLock.unlock();
            System.out.println("[" + Thread.currentThread().getName() + "] 释放写锁(完成降级)");
        }

        try {
            // 5. 使用数据(读操作)
            System.out.println("[" + Thread.currentThread().getName() + "] 在降级保护下使用数据");
            useData();
        } finally {
            // 6. 释放读锁
            readLock.unlock();
            System.out.println("[" + Thread.currentThread().getName() + "] 释放读锁");
        }
    }

    private void prepareData() {
        // 模拟数据准备(写操作)
        criticalData = (int) (Math.random() * 1000);
        dataValid = true;
        sleep(500); // 模拟耗时操作
    }

    private void useData() {
        if (!dataValid) {
            System.err.println("数据无效!");
            return;
        }
        
        // 模拟数据使用(读操作)
        System.out.println(">>> 使用关键数据: " + criticalData + " <<<");
        sleep(300);
    }

    // 干扰线程:尝试修改数据
    public void disturb() {
        writeLock.lock();
        try {
            System.out.println("\t[" + Thread.currentThread().getName() + "] 干扰线程获取写锁!");
            criticalData = -1; // 破坏数据
            dataValid = false;
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        LockDowngradeDemo demo = new LockDowngradeDemo();
        
        // 主工作线程(执行锁降级)
        new Thread(() -> demo.processWithDowngrade(), "MainWorker").start();
        
        sleep(100); // 确保主线程先启动
        
        // 干扰线程
        new Thread(() -> {
            System.out.println("\t[Disturber] 尝试干扰...");
            demo.disturb();
            System.out.println("\t[Disturber] 干扰完成");
        }, "Disturber").start();
    }
    
    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

执行效果说明:

[MainWorker] 获取写锁,准备数据...
[Disturber] 尝试干扰...     // 干扰线程启动
[MainWorker] 获取读锁(准备降级)
[MainWorker] 释放写锁(完成降级)
[MainWorker] 在降级保护下使用数据
>>> 使用关键数据: 742 <<<   // 数据未被干扰
[MainWorker] 释放读锁
[Disturber] 干扰线程获取写锁! // 此时才获取写锁
[Disturber] 干扰完成

4.3 错误示例:忘记锁降级

public void flawedProcess() {
    writeLock.lock();
    try {
        prepareData();
    } finally {
        writeLock.unlock();  // 危险:先释放写锁
    }
    
    // 此时其他线程可能修改数据!
    readLock.lock();  
    try {
        useData();  // 可能使用过期数据
    } finally {
        readLock.unlock();
    }
}

风险分析:

时间线:
1. 线程A获取写锁
2. 线程A修改数据
3. 线程A释放写锁
4. [危险间隙]
5. 线程B获取写锁
6. 线程B修改数据
7. 线程B释放写锁
8. 线程A获取读锁
9. 线程A读取到过期数据(线程B修改后的数据)
0基础手把手搭建 Trae\+Seedance2\.0 真人短剧生成智能体(可直接运行) 前言:本文专为0基础新手编写,全程无复杂代码、无专业术语,一步一截图提示(操作路径清晰),只需跟着点鼠标、复制粘贴,就能搭建出可自动生成真人短剧的智能体,生成的短剧可直接导出发布。核心工具:Trae(字节跳动AI IDE,免费)+ Seedance2.0(真人视频生成API,公测免费),无需提前学习编程、设计,全程10-15分钟搞定。 适用人群:0基础小白、想快速生成真人短剧(自媒体、短视频创作者)、无需懂技术即可实现自动化短剧生成的人群。 阅读详情

相关推荐

Camera AF和FF

Camera按对焦功能可以分为FF(Fixed Focus)和AF(Auto Focus),即固定对焦和自动对焦。固定对焦顾名思义就是焦距是固定的,而自动对焦可以通过移动镜头位置来改变焦距从而实现自动对焦。以前摄像的功能机大多是固定对焦,现在的手机都带有自动对焦功能。 FF模组包括镜头(Lens),传感器(Sensor),红外滤色器(IR Filter),电路板(PCB),镜头支撑(Lens Holder),连接器(Connector)。 Fig 1. FF Module AF模组包括FF模组,音圈马达(V

Coppa的博客 1万+

简洁明了的ReentrantReadWriteLock总结

一个ReentrantReadWriteLock同时只能存在一个写锁但是可以存在多个读锁,但不能同时存在写锁和读锁。 也即一个资源可以被多个读操作访问或一个写操作访问,但两者不能同时进行.如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁

qq_51033936的博客 878

arcgis建筑数据的矢量化(一)

简单来说,建筑数据的获取无外乎是矢量化和去网站下载 无奈的是,osm和网上的建筑数据只有全国的主要城市,实在是为难我们这些小城市的,百度和天地图上是有建筑数据,无奈人家不要开放下载啊,所以就只能矢量化咯, 矢量化,无外乎手动和自动,手动就是新建shp面手动勾绘,这里就不讲了,我们来看看自动矢量化 自动矢量化在arcgis中主要是利用arcscan和栅格转面(栅格转面这个效果真是一言难尽,所以我们主要讨论arcscan) 同时我们矢量化也不一定要局限于arcgis...

Jasenjane的博客 3927

ReentrantReadWriteLock锁降级的使用场合和代码示例

比如 缓存更新与读取: 在缓存系统中,一个线程可能需要更新缓存内容(需要写锁保护以防止数据不一致)。此时,如果支持锁降级,线程可以降级为读锁,允许其他线程也进行读取,提高缓存的访问效率。等待Writer-Reader Thread锁降级之后Reader Thread就能获得读锁了。当发生降级后, Writer-Reader Thread的写锁降级为读锁, 这时候。的场景,它可以减少不必要的独占锁时间,提高系统的并发性能。取消注释, 运行主程序后, 结果是。取消注释, 运行主程序后, 结果是。

weixin_45873353的博客 1218

【软碟通 | UltraISO】刻录光盘教程(制作U盘启动盘)

前言 今天老师说办公室的电脑有点卡,让我给重新安装一个 Windows 7 系统。于是就用软碟通(UltraISO)在 U 盘上刻录了一个 Windows 7 镜像,然后给老师重装了系统。顺便把制作 U 盘启动盘的教程也分享给大家。 软件 UltraISO v9.7.1.3519:点击跳转(密码:dd2k) 教程 将 U 盘插到电脑上,然后鼠标右击选择格式化(制作启动盘必须格式化) 点击文件-打开 选择下载好的系统镜像(如:Windows 7) 点击启动-写入硬盘映像 选择硬盘

XavierJ的博客 2万+

物联网组网技术优缺点对比:蓝牙、LoRa、wifi、NB-IoT、ZigBee等

1.前言 最近对物联网产品自组网技术选型,找到合适的组网技术,让产品自行组网并完成数据的传输。 2.参数对比

谁念西风独自凉 4万+

【Java并发】聊聊ReentrantReadWriteLock锁降级和StampedLock邮戳锁

聊聊ReentrantReadWriteLock锁降级和StampedLock邮戳锁

芝兰生于深谷,不以无人而不芳 626

把核心变干净的代价与回报:为什么推动 Clean Core,几乎绕不开 SAP Integration Suite 与 API Management

Clean Core的核心价值在于构建可持续升级的ERP系统架构,其关键不在于减少代码或限制扩展,而是通过解耦和治理实现系统边界的清晰划分。这一理念将推动企业从传统的"私搭乱建"式集成向平台化工程转型,而SAP Integration Suite和API Management正是实现这一转型的核心组件。 Integration Suite作为集成中枢,通过统一平台、可复用内容库和事件驱动架构,将碎片化连接转变为标准化能力;API Management则负责建立接口治理体系,通过统一入口、

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 243

Java 读写锁 之 锁降级

锁降级 :是指保持住当前的写锁(已拥有),再获取读锁,随后释放写锁的过程。 1.锁降级的用途 举一个场景:数据不常变化,多个线程可以对数据进行并发的处理,如果当前线程感知数据变化,则进行数据处理操作,而其他线程则会被阻塞,直至当前线程完成数据的准备操作,这时候就需要使用锁降级。 具体代码,update(是否更新的标志, false 标识可以更新,volatile修饰,使所有线程能够...

学习笔记 5376

COMSOL介电弹性体数值仿真指南:预拉伸圆形DEA的稳态变形分析(II)

本期博客是《COMSOL介电弹性体数值仿真指南》系列博客中的一期。聚焦于介电弹性体驱动器(DEA)中最为经典的预拉伸圆形DEA驱动器构型,对其在COMSOL平台中的具体建模步骤进行介绍与讨论。

忘了面孔的Batou 355

手把手教你学Simulink--基于多能源系统协同控制的复杂场景实例:氢燃料电池+锂电池混合动力电动汽车能量管理仿真

本文介绍了基于Simulink的氢燃料电池与锂电池混合动力电动汽车能量管理系统的建模与仿真方法。系统包含驾驶循环输入、燃料电池子系统、锂电池子系统、能量管理、电机驱动等模块,通过协同控制优化能源分配。详细说明了从模型创建到各子系统设置的7个步骤,并提供了MATLAB代码示例。该仿真可评估不同工况下的系统响应速度、能量利用效率和电池寿命等性能指标,为混合动力汽车的能量管理策略设计和优化提供参考。

MHD0815的博客 239

深入了解ReentrantReadWriteLock

ReentrantReadWriteLock称为读写锁,它提供一个读锁,支持多个线程共享同一把锁。它也提供了一把写锁,是独占锁,和其他读锁或者写锁互斥,表明只有一个线程能够持有锁资源。通过两把锁的协同工作,能够最大化的提高读写性能,特别是读多写少的场景,而往往大部分的场景都是读多写少的。线程进入读锁的前提条件:(1)没有其他线程的写锁(2)没有写请求,或者有写请求但调用线程和持有锁的线程是同一个线程线程进入写锁的前提条件:(1)没有其他线程的读锁(2)没有其他线程的写锁。

weixin_49131718的博客 297

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

【深入理解ReentrantReadWriteLock】读写分离与锁降级实践

锁降级(Lock Downgrading)是指线程在持有写锁获取读锁释放写锁在仅持有读锁的状态下继续操作// 标准锁降级流程 writeLock.lock();// 1.获取写锁 try {// 修改数据... readLock.lock();// 2.获取读锁(关键步骤) } finally {// 3.释放写锁(完成降级) } try {// 读取数据(受读锁保护) } finally {// 4.释放读锁 }通过读写分离高16位记录读锁数量,低16位记录写锁重入次数。

weixin_40390354的博客 57

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

ReentrantReadWriteLock 与 Condition:读写锁与精准唤醒机制

Day05 讲了 `ReentrantLock` 这种独占锁——同一时刻只能有一个线程持有。但现实里有大量"读多写少"的场景:一个缓存被上千个请求并发读取,写操作却一天也遇不到几次。用独占锁保护这种缓存,相当于把并发读也全部串行化了,性能白白浪费。

NPE2.0的博客 197

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【Java并发编程】锁机制:Lock体系:ReentrantLock、ReentrantReadWriteLock、Lock vs synchronized 区别(附《思维导图》+《面试高频考点清单》

本文系统梳理Java Lock体系核心知识:涵盖ReentrantLock(可重入、公平/非公平、AQS实现)、ReentrantReadWriteLock(读写分离、锁降级、state拆分)及StampedLock(乐观读、缓解写饥饿),深度对比synchronized与Lock在实现、特性、性能及场景上的八大区别,助力高并发编程与面试通关。

小江的记录本 372

ReentrantReadWriteLock:深入解析与最佳实践

在并发编程中,锁是用于控制多个线程对共享资源的访问的一种机制。读写锁是其中一种特殊的锁,它分为读锁和写锁两部分,允许多个线程同时获得读锁,而写锁是互斥锁,不允许多个线程同时获得。Java并发包提供了ReentrantReadWriteLock类作为读写锁的实现。本文将对ReentrantReadWriteLock进行详细分析,并探讨其最佳实践。ReentrantReadWriteLock是一种重要的并发工具,它可以提高多读少写的场景的并发性能。

小阳同学的博客 1560

04Lock 接口及实现类详解:从 ReentrantLock 到并发场景实践

Lock 接口是 Java 并发包对“锁”机制的抽象封装,它将锁的“获取”与“释放”解耦为独立的方法,摆脱了 synchronized 对代码块、方法的绑定,为开发者提供了更精细的控制能力。与 synchronized 的“隐式同步”不同,Lock 采用“显式同步”,开发者需手动控制锁的获取与释放,这既是灵活性的体现,也对编码严谨性提出了更高要求。

2501_90914799的博客 531

Java并发——Lock锁

锁类型特点简单、自动释放,但功能单一,不可中断,无法公平手动控制锁,支持公平锁、可中断、限时等待,可重入读写分离,读读并发,写写互斥,适用于读多写少场景,支持锁降级在实际开发中,应根据具体需求选择合适的锁:简单的同步逻辑,优先使用。需要高级功能(公平、超时、可中断)时,选用。读多写少的场景,使用能显著提升并发性能。掌握这些锁的特性,能够帮助我们编写出更高效、更可靠的多线程程序。

2301_82251392的博客 409

「JUC」并发协作工具箱:读写锁、信号量与循环屏障实战

本文介绍Java并发包JUC核心工具,先分析死锁等活跃性问题及排查、预防方法。对比ReentrantLock相对synchronized的优势,讲解ReentrantReadWriteLock读写分离、AQS位运算与锁降级,介绍StampedLock乐观读。同时阐述Semaphore限流原理,以及CountDownLatch、CyclicBarrier线程协同机制,结合实战场景,助力高并发代码开发。

Drifter_yh的博客 539

ReentrantReadWriteLock之锁降级

概念 **锁降级是指在读写锁中,为了保证写后数据的一致性,将写锁降级为读锁。**其实就是利用了读写锁的互斥性。 代码 public class DegradeLock { private int value; private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private Lock re...

fany000的博客 417
上一篇: Python量化交易一体化解决方案
下一篇: 从Tushare到Wind,散户的Python量化系统搭建实录
觉醒赵小四
博客等级 码龄9年 373粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值