思路:100个线程抢3张券。多线程同时操作共享资源,也就是库存,不能超卖。
查询库存(缓存中有从缓存取,缓存没有就查询DB,再缓存起来)(在读锁中实现)
库存>0,更新DB,并删除缓存(放在写锁中实现)。
java
package com.niuniu.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.niuniu.common.vo.Response;
import com.niuniu.order.model.Order;
public interface CouponService extends IService<Order> {
/**
* 100个人抢三张券,每人限购一张
* @return
*/
Response dealCoupon(Long productId, Integer num);
}
java
package com.niuniu.order.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.niuniu.common.vo.Response;
import com.niuniu.order.feignclient.ProductStoreClient;
import com.niuniu.order.mapper.OrderMapper;
import com.niuniu.order.model.Order;
import com.niuniu.order.service.CouponService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Objects;
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<OrderMapper, Order> implements CouponService {
@Autowired
private ProductStoreClient productStoreClient;
@Resource
private RedisTemplate<String, String> redisTemplate;
@Resource
private RedissonClient redissonClient;
/**
* 抢券逻辑
* @return
*/
@Override
public Response dealCoupon(Long productId, Integer num) {
// 1、查询库存
Integer stock = this.getStockById(productId);
System.out.println(Thread.currentThread().getName()+"读到的剩余量是"+stock);
if (stock > 0){
// 减少库存
this.updateStock(productId, num);
}
return Response.ok();
}
/**
* 根据商品id查询库存
* @param productId
* @return
*/
private Integer getStockById(Long productId){
RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock("READ_WRITE_STOCK_" + productId);
RLock rLock = rReadWriteLock.readLock();
Integer stock = 0;
try{
rLock.lock();
String stockObj = redisTemplate.opsForValue().get("STOCK_" + productId);
// 从缓存中取
if (Objects.nonNull(stockObj)) {
stock = Integer.parseInt(stockObj);
} else { // 从数据库查询并放入缓存
Response<Integer> response = productStoreClient.getStockById(productId);
if (Objects.isNull(response)){
throw new RuntimeException("查询商品库存失败!");
}
stock = response.getBody();
redisTemplate.opsForValue().set("STOCK_" + productId, String.valueOf(stock));
}
} catch (Exception e){
e.printStackTrace();
} finally {
rLock.unlock();
}
return stock;
}
/**
* 更新DB,删除缓存
* @param productId
* @param num
*/
private void updateStock(Long productId, Integer num){
RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock("READ_WRITE_STOCK_" + productId);
RLock wLock = rReadWriteLock.writeLock();
try {
wLock.lock();
Integer stock = this.getStockById(productId);
if (stock > 0) {
// 更新DB
productStoreClient.updateStockById(productId, num);
// 删除缓存
redisTemplate.delete("STOCK_" + productId);
System.out.println(Thread.currentThread().getName() + ",抢到券了");
}
} finally {
wLock.unlock();
}
}
}
java
@GetMapping("/dealCoupon")
public Response dealCoupon(){
// 100个人抢三张券,每人限购一张
for (int i = 0; i < 100; i++) {
new Thread(()->{
couponService.dealCoupon(1L, 1);
}).start();
}
return Response.ok();
}