用 Redis 的 List 存储库存队列,并通过 LPOP 原子性出队来保证并发安全案例
用 Jedis 作为 Redis 客户端库(简单易用),实现一个模拟秒杀库存扣减的案例。
Java 代码示例
import redis.clients.jedis.Jedis;public class RedisStockQueue {private static final String STOCK_KEY = "product:1001:stock";public static void main(String[] args) {// 连接 RedisJedis jedis = new Jedis("127.0.0.1", 6379);jedis.auth("your_password"); // 如果有密码// 初始化库存队列(假设库存 5 件)initStock(jedis, 5);// 模拟多个用户并发抢购for (int i = 1; i <= 10; i++) {String userId = "user_" + i;new Thread(() -> {String result = buyProduct(jedis, userId);System.out.println(userId + " -> " + result);}).start();}jedis.close();}/*** 初始化库存队列*/private static void initStock(Jedis jedis, int stockCount) {jedis.del(STOCK_KEY); // 清空旧库存for (int i = 1; i <= stockCount; i++) {jedis.rpush(STOCK_KEY, "stock_" + i);}System.out.println("库存初始化完成,数量:" + stockCount);}/*** 用户抢购商品*/private static String buyProduct(Jedis jedis, String userId) {// LPOP 原子性出队String stockItem = jedis.lpop(STOCK_KEY);if (stockItem != null) {return "抢购成功,获得库存:" + stockItem;} else {return "抢购失败,库存已空";}}
}
代码说明
库存初始化
- 用
RPUSH将库存数据压入 Redis List,例如stock_1、stock_2... - List 的顺序可以代表库存的唯一标识。
- 用
抢购逻辑
- 用
LPOP从队列头部取出一个库存项。 LPOP是 Redis 的原子操作,即使多个线程同时执行,也不会出现超卖。
- 用
并发安全
- Redis 的单线程模型保证了
LPOP的原子性,不需要额外加锁。
- Redis 的单线程模型保证了
运行效果示例
假设库存是 5 件,10 个用户同时抢购,输出可能是:
库存初始化完成,数量:5
user_1 -> 抢购成功,获得库存:stock_1
user_3 -> 抢购成功,获得库存:stock_2
user_5 -> 抢购成功,获得库存:stock_3
user_2 -> 抢购成功,获得库存:stock_4
user_4 -> 抢购成功,获得库存:stock_5
user_6 -> 抢购失败,库存已空
user_7 -> 抢购失败,库存已空
user_8 -> 抢购失败,库存已空
user_9 -> 抢购失败,库存已空
user_10 -> 抢购失败,库存已空
