如何实现一个分布式锁?------来自 Java 老兵的实战总结 🚀🔐
作者:写了 8 年 Java,踩了 8 年 Redis 的坑,终于把锁写顺的一位打工人
标签:#Java #分布式锁 #Redis #并发控制 #工具类封装
🧠 前言:为啥需要分布式锁?
很多人第一次听说"分布式锁"时会困惑:为啥不能用 synchronized
或 ReentrantLock
呢?
因为它们只能保证单进程、多线程之间互斥 ,在分布式环境下(比如多个服务部署在不同机器),它们就失效了。
✅ 分布式锁的常见场景:
- 防止重复下单(秒杀、抢购)
- 限制库存扣减并发
- 控制定时任务在集群中只执行一次
- 保证接口幂等性
🔧 分布式锁常见实现方式
实现方式 | 优点 | 缺点 |
---|---|---|
Redis | 快速、轻量、适合短时锁 | 主从同步延迟可能导致锁失效 |
ZooKeeper | CP模型,强一致性 | 实现复杂、性能略低 |
数据库 | 利用唯一索引或悲观锁 | 性能差、不适合高并发 |
今天我们重点讲解:基于 Redis 实现一个可复用的分布式锁工具类。
🚧 分布式锁的五大关键点
- 互斥性:同一时间只能有一个客户端获取锁
- 防死锁:锁必须设置过期时间
- 唯一标识:释放锁时必须验证是自己加的锁
- 原子性:加锁和设置过期时间必须是原子操作
- 高可用性:客户端必须具备重试机制
🛠️ 分布式锁工具类(基于 Redis)
我们使用的是最常见的单节点 Redis + Jedis
客户端实现(可替换为 Redisson
、Lettuce
)。
📦 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!🔥