Java读写锁降级

锁降级并不是读锁 / 写锁有 "等级数值",而是指锁的 "独占 / 共享" 状态的转换,且只能从 "更严格的独占状态" 降到 "宽松的共享状态"。

一、先搞懂:为什么会有 "锁降级" 这个说法?

读写锁里的 "等级",本质是锁的 "排他性强度":

  • 写锁(排他锁):强度最高 → 独占资源,不允许任何线程(读 / 写)同时访问。
  • 读锁(共享锁):强度更低 → 允许其他读线程共享资源,只排斥写线程。

"锁降级" 就是:持有写锁的线程,在不释放写锁的前提下先获取读锁,再释放写锁,最终只持有读锁。这个过程是 "从高强度的独占锁,降到低强度的共享锁",所以叫 "降级"。

反过来,"锁升级"(读锁→写锁)是不被允许的 ------ 如果一个线程先拿了读锁,再尝试拿写锁,会直接导致死锁(自己等自己释放读锁,其他读线程也在占用,永远等不到)。

二、锁降级的核心目的:保证数据一致性

锁降级不是 "炫技",而是为了在写操作完成后,安全地读取自己刚写入的数据,且期间不让其他写线程插队修改数据。

举个生活例子:

你是仓库管理员(线程),拿着 "仓库钥匙(写锁)" 进去修改库存(写操作)。改完后,你想确认一下改得对不对(读操作),这时候:

  • 如果你先把钥匙还了(释放写锁),再去拿 "查看权限(读锁)",中间可能有其他管理员(其他线程)插队拿钥匙改库存,你看到的就不是自己刚改的数了。
  • 如果你先拿 "查看权限(读锁)",再还钥匙(释放写锁),中间没有空窗期,能确保你看的是自己刚改的数,之后也能和其他员工(读线程)一起查看。
java 复制代码
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockDowngradeDemo {
    private final Map<String, String> cache = new HashMap<>();
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();

    // 锁降级的核心方法
    public String updateAndRead(String key, String value) {
        // 1. 先获取写锁(高强度独占锁),执行写操作
        writeLock.lock();
        String result = null;
        try {
            System.out.println(Thread.currentThread().getName() + " 持有写锁,执行写操作");
            cache.put(key, value); // 写入数据

            // 2. 关键:在释放写锁前,先获取读锁(降级的核心步骤)
            // 这一步不会阻塞,因为当前线程已经持有写锁,读写锁允许写线程获取读锁
            readLock.lock();
            System.out.println(Thread.currentThread().getName() + " 成功获取读锁,准备降级");
        } finally {
            // 3. 释放写锁,此时线程只持有读锁(完成降级)
            writeLock.unlock();
            System.out.println(Thread.currentThread().getName() + " 释放写锁,完成锁降级");
        }

        // 4. 持有读锁读取数据,此时其他写线程无法修改,读线程可以共享
        try {
            System.out.println(Thread.currentThread().getName() + " 持有读锁,读取数据:" + cache.get(key));
            result = cache.get(key);
        } finally {
            // 5. 最后释放读锁
            readLock.unlock();
            System.out.println(Thread.currentThread().getName() + " 释放读锁");
        }
        return result;
    }

    public static void main(String[] args) {
        LockDowngradeDemo demo = new LockDowngradeDemo();
        // 单线程执行锁降级
        new Thread(() -> demo.updateAndRead("user", "张三"), "线程A").start();

        // 多线程验证:线程A降级后,线程B可以获取读锁,但线程C的写锁会被阻塞
        new Thread(() -> {
            demo.readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 拿到读锁,读取user:" + demo.cache.get("user"));
            } finally {
                demo.readLock.unlock();
            }
        }, "线程B(读)").start();

        new Thread(() -> {
            demo.writeLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 拿到写锁,修改user为李四");
                demo.cache.put("user", "李四");
            } finally {
                demo.writeLock.unlock();
            }
        }, "线程C(写)").start();
    }
}

线程A 持有写锁,执行写操作

线程A 成功获取读锁,准备降级

线程A 释放写锁,完成锁降级

线程A 持有读锁,读取数据:张三

线程B(读) 拿到读锁,读取user:张三 // 线程B和A共享读锁,并行执行

线程A 释放读锁

线程C(写) 拿到写锁,修改user为李四 // 只有读锁都释放后,写锁才会执行


text 复制代码
线程操作流程:
1. 拿写锁 → 独占资源,执行写操作(此时所有读/写线程阻塞)
2. 拿读锁 → 因为当前线程持有写锁,这一步不会阻塞(读写锁的可重入特性)
3. 释放写锁 → 此时线程只持有读锁(完成降级),其他读线程可以加读锁,但写线程仍阻塞
4. 执行读操作 → 安全读取自己刚写的数据
5. 释放读锁 → 所有线程可正常竞争锁
相关推荐
AI你一生一世1 小时前
不止于时序存储:DolphinDB 如何成为 AI 时代的工业智能底座
java·人工智能·struts·时序数据库·工业智能·dolphindb·ai基础设施
belldeep1 小时前
python:genesis-world, Genesis-Embodied-AI
人工智能·python·ai·genesis
布莱克6051 小时前
C/C++ 中变量的声明与定义:区别、联系与常见误区
c语言·开发语言·c++·声明·定义
云-缩影2 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
LabVIEW开发2 小时前
LabVIEW 局部变量为何引发竞争条件?四种同步机制
开发语言·labview
瑞克-桑切斯2 小时前
7、MySQL 慢查询排查与优化实战指南
java·服务器·数据库
benchmark_cc2 小时前
A 股、港股、美股如何建立统一的标的主数据格式?
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
维克兜率天3 小时前
【维克】趋势型CTA:期货市场的“追涨杀跌“
人工智能·python
Zenova EdgeOS3 小时前
Go 工业边缘错误处理实战:用 errors 构建可追溯错误链
开发语言·数据库·golang
AI开源软件4 小时前
SpringBoot3+Vue3 财务管理系统如何设计:从业务单据到总账的模块划分
spring boot·vue3·领域建模·会计凭证·ruoyi office·业财协同·财务系统设计