Redis 实现商品抢购秒杀
一、 写在前面
√ \color{#FF7D00}{√} √ 写出来,像是单例写法里的双重检查
√ \color{#FF7D00}{√} √ 大佬们如果有更好的方法或建议,可以提一提,博主萌新一个
二、代码
// 商品列表
collectionIdList = new ArrayList<>();
static {
collectionIdList.add(1L);
collectionIdList.add(2L);
collectionIdList.add(3L);
collectionIdList.add(4L);
collectionIdList.add(5L);
collectionIdList.add(6L);
collectionIdList.add(7L);
collectionIdList.add(8L);
collectionIdList.add(9L);
}
/**
* 开启预售
*/
public void preSale() {
// 商品放入redis库存
for (Long collectionId : collectionIdList) {
redisTemplate.opsForValue().set("STOCK_COLLECTION_ID_" + collectionId, collectionId);
}
}
/**
* 开始抢购
*/
public void purchase() {
String requestId = UUID.randomUUID().toString();
// 是否抢到标志位
boolean flag = false;
for (Long collectionId : collectionIdList) {
// 已被其他用户抢到,抢下一个
if (redisTemplate.opsForValue().get("SELL_COLLECTION_ID_" + collectionId) != null
|| redisTemplate.opsForValue().get("STOCK_COLLECTION_ID_" + collectionId) == null) {
continue;
}
// 已被其他用户锁住,抢下一个
if (!redisLock.tryLock("COLLECTION_LOCK_" + collectionId, requestId, 300L, TimeUnit.SECONDS)) {
continue;
}
// 可能抢到藏品,锁住
else {
try {
// 已被其他用户抢到,释放锁,抢下一个
if (redisTemplate.opsForValue().get("SELL_COLLECTION_ID_" + collectionId) != null
|| redisTemplate.opsForValue().get("STOCK_COLLECTION_ID_" + collectionId) == null) {
redisLock.releaseLock("COLLECTION_LOCK_" + collectionId, requestId);
continue;
}
// 宣示藏品5min所有权,5min内未支付,则放弃所有权
LoggerHolder.getLogger().info("抢到了 {} 藏品!", collectionId);
redisTemplate.opsForValue().set("SELL_COLLECTION_ID_" + collectionId, "123", 300L, TimeUnit.SECONDS);
flag = true;
break;
} finally {
redisLock.releaseLock("COLLECTION_LOCK_" + collectionId, requestId);
}
}
}
if (!flag) {
throw new ServiceException("未抢到藏品!");
}
}