如何实现一个分布式锁?——来自 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!🔥

相关推荐
种子q_q几秒前
组合索引、覆盖索引、聚集索引、非聚集索引的区别
后端·面试
码事漫谈1 分钟前
WaitForSingleObject 函数参数影响及信号处理分析
后端
ffutop2 分钟前
gRPC mTLS 问题调试指南
后端
讨厌吃蛋黄酥3 分钟前
利用Mock实现前后端联调的解决方案
前端·javascript·后端
JavaArchJourney4 分钟前
Spring Cloud 微服务架构
后端
爱吃小土豆豆豆豆16 分钟前
登录校验一
java·大数据·数据库
热河暖男16 分钟前
Spring Boot AI 极速入门:解锁智能应用开发
java·人工智能·spring boot·ai编程
lifallen19 分钟前
hadoop.yarn 带时间的LRU 延迟删除
java·大数据·数据结构·hadoop·分布式·算法
钮钴禄·爱因斯晨23 分钟前
赛博算命之八字测算事业运势的Java实现(四柱、五行、十神、流年、格局详细测算)
java·开发语言·aigc
都叫我大帅哥32 分钟前
TOGAF揭秘:为什么全球80%的500强企业用它规划IT摩天大楼?
java