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

相关推荐
有你有我OK7 分钟前
springboot Admin 服务端 客户端配置
spring boot·后端·elasticsearch
xiaoopin2 小时前
简单的分布式锁 SpringBoot Redisson‌
spring boot·分布式·后端
默 语5 小时前
MySQL中的数据去重,该用DISTINCT还是GROUP BY?
java·数据库·mysql·distinct·group by·1024程序员节·数据去重
oDeviloo7 小时前
新版IntelliJ IDEA个性化设置兼容老版习惯
java·ide·intellij-idea
一只小透明啊啊啊啊7 小时前
Java Web 开发的核心组件:Servlet, JSP,Filter,Listener
java·前端·servlet
你的人类朋友7 小时前
设计模式有哪几类?
前端·后端·设计模式
Yeats_Liao7 小时前
Go Web 编程快速入门 10 - 数据库集成与ORM:连接池、查询优化与事务管理
前端·数据库·后端·golang
spencer_tseng8 小时前
Eclipse Uninstall Software
java·ide·eclipse
嗯、.8 小时前
使用 iText 9 为 PDF 添加文字水印的完整实战
java·pdf·itext
拉不动的猪8 小时前
函数组件和异步组件
前端·javascript·面试