使用redis读写锁实现抢券功能
思路:100个线程抢3张券。多线程同时操作共享资源,也就是库存,不能超卖。
查询库存(缓存中有从缓存取,缓存没有就查询DB,再缓存起来)(在读锁中实现)
库存>0,更新DB,并删除缓存(放在写锁中实现)。
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);
}
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 {@Autowiredprivate ProductStoreClient productStoreClient;@Resourceprivate RedisTemplate<String, String> redisTemplate;@Resourceprivate RedissonClient redissonClient;/*** 抢券逻辑* @return*/@Overridepublic 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) {// 更新DBproductStoreClient.updateStockById(productId, num);// 删除缓存redisTemplate.delete("STOCK_" + productId);System.out.println(Thread.currentThread().getName() + ",抢到券了");}} finally {wLock.unlock();}}
}
@GetMapping("/dealCoupon")public Response dealCoupon(){// 100个人抢三张券,每人限购一张for (int i = 0; i < 100; i++) {new Thread(()->{couponService.dealCoupon(1L, 1);}).start();}return Response.ok();}