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. 释放读锁 → 所有线程可正常竞争锁
相关推荐
流云细水1 小时前
Spec(规格说明书)与Skill(技能库)实操指南
java·人工智能
毕设源码-钟学长1 小时前
【开题答辩全过程】以 基于Java的停车场信息管理系统设计与实现为例,包含答辩的问题和答案
java·开发语言
Mr -老鬼1 小时前
基于 Go 的脚本平台 APP 云控系统
开发语言·后端·golang
rannn_1111 小时前
【苍穹外卖|Day7】缓存菜品、缓存套餐、添加购物车、查看购物车、清空购物车
java·spring boot·redis·后端·缓存·项目
晔子yy1 小时前
带你了解Java中的Mono接口
java·数据库·oracle
hewence11 小时前
Kotlin CoroutineScope解密
android·开发语言·kotlin
咩图1 小时前
VSCode+Python创建项目
开发语言·python
zhanglu51161 小时前
Java Lambda 表达式使用深度解析
开发语言·前端·python
zhougl9961 小时前
Springboot - druid 连接池
java·spring boot·后端