前言
下面是一个用 Java 实现的分布式锁示例,基于 Redis 来确保库存扣减的原子性,避免超卖问题。我们将使用 Jedis 作为 Redis 客户端,并模拟一个电商系统中的库存扣减场景。
1. 环境准备
在运行代码之前,请确保以下环境已准备好:
- 安装 Redis 并启动 Redis 服务。
- 在项目中引入 Jedis 依赖(Maven 配置如下):
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
2. Java 实现
以下是完整的 Java 代码实现:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.UUID;
public class DistributedLockExample {
private static final String REDIS_HOST = "localhost";
private static final int REDIS_PORT = 6379;
private static final String LOCK_KEY = "lock:item_123"; // 锁的键
private static final String STOCK_KEY = "stock:item_123"; // 库存的键
private static final int STOCK_INITIAL = 10; // 初始库存
public static void main(String[] args) {
// 初始化 Redis 连接
try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT)) {
// 初始化库存
jedis.set(STOCK_KEY, String.valueOf(STOCK_INITIAL));
// 模拟多个用户并发购买
for (int i = 1; i <= 15; i++) { // 15 个用户尝试购买
String userId = "user_" + i;
new Thread(() -> purchaseItem(jedis, userId)).start();
}
}
}
/**
* 购买商品
*
* @param jedis Redis 客户端
* @param userId 用户 ID
*/
private static void purchaseItem(Jedis jedis, String userId) {
String lockValue = UUID.randomUUID().toString(); // 锁的值,确保唯一性
int retryCount = 3; // 重试次数
boolean lockAcquired = false;
// 尝试获取锁
while (retryCount > 0) {
String result = jedis.set(LOCK_KEY, lockValue, SetParams.setParams().nx().ex(10)); // nx: 键不存在时才设置,ex: 锁的过期时间为 10 秒
if ("OK".equals(result)) {
lockAcquired = true;
break;
}
retryCount--;
try {
Thread.sleep(1000); // 等待 1 秒后重试
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!lockAcquired) {
System.out.println(userId + " 获取锁失败,无法购买");
return;
}
try {
// 查询库存
int stock = Integer.parseInt(jedis.get(STOCK_KEY));
if (stock <= 0) {
System.out.println(userId + " 购买失败,库存不足");
return;
}
// 扣减库存
jedis.decr(STOCK_KEY);
System.out.println(userId + " 购买成功,库存剩余: " + (stock - 1));
} finally {
// 释放锁
if (lockValue.equals(jedis.get(LOCK_KEY))) {
jedis.del(LOCK_KEY);
}
}
}
}
3. 代码说明
-
Redis 连接:
- 使用 Jedis 客户端连接 Redis,并初始化库存。
-
分布式锁的实现:
- 使用
jedis.set
方法,结合nx
(键不存在时才设置)和ex
(设置过期时间)参数,实现分布式锁。
- 使用
-
库存扣减:
- 在获取锁后,查询库存并扣减库存,确保操作的原子性。
-
锁的释放:
- 在 finally 块中释放锁,确保锁一定会被释放,避免死锁。
-
重试机制:
- 如果获取锁失败,用户会等待并重试,直到成功获取锁或重试次数用尽。
4. 运行结果
运行代码后,输出可能如下:
user_1 购买成功,库存剩余: 9
user_2 购买成功,库存剩余: 8
user_3 购买成功,库存剩余: 7
user_4 购买成功,库存剩余: 6
user_5 购买成功,库存剩余: 5
user_6 购买成功,库存剩余: 4
user_7 购买成功,库存剩余: 3
user_8 购买成功,库存剩余: 2
user_9 购买成功,库存剩余: 1
user_10 购买成功,库存剩余: 0
user_11 购买失败,库存不足
user_12 购买失败,库存不足
user_13 获取锁失败,无法购买
user_14 获取锁失败,无法购买
user_15 获取锁失败,无法购买
5. 总结
通过 Java 和 Redis 实现分布式锁,可以有效解决电商系统中的库存扣减问题,避免超卖现象。