如何实现一个分布式锁?——来自 Java 老兵的实战总结 🚀🔐

如何实现一个分布式锁?------来自 Java 老兵的实战总结 🚀🔐

作者:写了 8 年 Java,踩了 8 年 Redis 的坑,终于把锁写顺的一位打工人

标签:#Java #分布式锁 #Redis #并发控制 #工具类封装


🧠 前言:为啥需要分布式锁?

很多人第一次听说"分布式锁"时会困惑:为啥不能用 synchronizedReentrantLock 呢?

因为它们只能保证单进程、多线程之间互斥 ,在分布式环境下(比如多个服务部署在不同机器),它们就失效了。

✅ 分布式锁的常见场景:

  • 防止重复下单(秒杀、抢购)
  • 限制库存扣减并发
  • 控制定时任务在集群中只执行一次
  • 保证接口幂等性

🔧 分布式锁常见实现方式

实现方式 优点 缺点
Redis 快速、轻量、适合短时锁 主从同步延迟可能导致锁失效
ZooKeeper CP模型,强一致性 实现复杂、性能略低
数据库 利用唯一索引或悲观锁 性能差、不适合高并发

今天我们重点讲解:基于 Redis 实现一个可复用的分布式锁工具类。


🚧 分布式锁的五大关键点

  1. 互斥性:同一时间只能有一个客户端获取锁
  2. 防死锁:锁必须设置过期时间
  3. 唯一标识:释放锁时必须验证是自己加的锁
  4. 原子性:加锁和设置过期时间必须是原子操作
  5. 高可用性:客户端必须具备重试机制

🛠️ 分布式锁工具类(基于 Redis)

我们使用的是最常见的单节点 Redis + Jedis 客户端实现(可替换为 RedissonLettuce)。

📦 Maven 依赖

xml 复制代码
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>4.4.3</version>
</dependency>

💡 RedisDistributedLock.java

typescript 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.util.UUID;

public class RedisDistributedLock {

    private final Jedis jedis;

    // 默认锁过期时间(毫秒)
    private static final int DEFAULT_EXPIRE_TIME = 10000;

    public RedisDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }

    /**
     * 获取锁
     * @param lockKey 锁的 key
     * @param requestId 请求标识(唯一性)
     * @param expireTime 锁过期时间(毫秒)
     * @return 是否成功获取锁
     */
    public boolean tryLock(String lockKey, String requestId, int expireTime) {
        SetParams params = new SetParams();
        params.nx().px(expireTime);
        String result = jedis.set(lockKey, requestId, params);
        return "OK".equals(result);
    }

    /**
     * 释放锁(必须验证是自己的锁)
     * @param lockKey 锁的 key
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String requestId) {
        String luaScript =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "return redis.call('del', KEYS[1]) " +
                "else return 0 end";

        Object result = jedis.eval(luaScript, 1, lockKey, requestId);
        return Long.valueOf(1).equals(result);
    }

    /**
     * 获取随机 requestId(UUID)
     */
    public String generateRequestId() {
        return UUID.randomUUID().toString();
    }
}

✅ 如何使用?Demo 来了!

🧪 DistributedLockDemo.java

csharp 复制代码
import redis.clients.jedis.Jedis;

public class DistributedLockDemo {

    public static void main(String[] args) {

        Jedis jedis = new Jedis("localhost", 6379);
        RedisDistributedLock lock = new RedisDistributedLock(jedis);

        String lockKey = "lock:order:123456";
        String requestId = lock.generateRequestId();

        try {
            boolean acquired = lock.tryLock(lockKey, requestId, 5000);
            if (acquired) {
                System.out.println("获取锁成功,开始执行业务逻辑");

                // 模拟业务处理
                Thread.sleep(3000);

                System.out.println("业务处理完成,准备释放锁");
            } else {
                System.out.println("获取锁失败,稍后重试");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boolean released = lock.releaseLock(lockKey, requestId);
            if (released) {
                System.out.println("释放锁成功");
            } else {
                System.out.println("释放锁失败,可能不是当前线程持有");
            }
        }

        jedis.close();
    }
}

⚠️ 注意事项

  • requestId 必须唯一且线程安全,防止误删别人的锁
  • 必须设置过期时间,防止死锁
  • 释放锁必须原子操作,所以用了 Lua 脚本
  • 生产环境建议使用 Redisson,更健壮、支持 Watchdog

🚀 总结

分布式锁虽然听起来高大上,但本质就是**"在分布式环境下保证某段代码或资源的互斥访问"**。

你只需要记住:

分布式锁 = 加锁 + 可过期 + 可识别 + 可释放 + 可重试

通过封装成工具类,我们可以在任何业务场景中轻松使用,提升系统并发控制能力、避免竞态条件。


💬 最后

想要用 Java 写出靠谱的分布式系统,分布式锁你必须懂!

希望这篇文章能让你在面试和实战中都能自信回答:

"分布式锁?我自己写过一个!"

关注我,一起把 Java 写成架构图,写成 offer!🔥

相关推荐
用户79117724235832 分钟前
黑马点评秒杀优化和场景补充
后端
猎豹奕叔6 分钟前
设计模式的重要设计原则,建议收藏
后端
sophie旭10 分钟前
一道面试题,开始性能优化之旅(5)-- 浏览器和性能
前端·面试·性能优化
笨手笨脚の19 分钟前
设计模式-装饰器模式
java·设计模式·装饰器模式·结构型设计模式
低音钢琴24 分钟前
【碎片化学习】SpringBoot中的自动配置(Auto Configuration)
spring boot·后端
9毫米的幻想33 分钟前
【Linux系统】—— 程序地址空间
java·linux·c语言·jvm·c++·学习
C++chaofan37 分钟前
Redisson分布式限流
java·jvm·spring boot·redis·分布式·mvc·redisson
whltaoin41 分钟前
Java 网络请求 Jar 包选型指南:从基础到实战
java·http·okhttp·网络请求·retrofit
疯癫的老码农1 小时前
【word解析】从OLE到OMML:公式格式转换的挑战与解决方案
java·spring boot·spring·word
canonical-entropy1 小时前
集成NopReport动态生成Word表格
后端·低代码·函数式编程·可逆计算·nop平台