天天看點

synchronized的不足與redis分布式鎖的使用

關鍵代碼:

@Component
@Slf4j
public class RedisLock {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 加鎖
     * @param key 商品id
     * @param value 目前時間+逾時時間
     * @return
     */
    public boolean lock(String key, String value) {
        //setIfAbsent()也就是redis的setnx,當key不存在時設定value
        if(redisTemplate.opsForValue().setIfAbsent(key, value)) {
            //加鎖成功
            return true;
        }
        //當鎖已存在,可以擷取該鎖的value,來判斷是否過期
        String currentValue = redisTemplate.opsForValue().get(key);
        //如果鎖過期
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //擷取上一個鎖的時間
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            //如果多個線程同時進入這裡,則可以通過判斷oldValue與currentValue是否相等來限制多個線程加鎖
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 解鎖
     * @param key
     * @param value
     */
    public void unlock(String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e) {
            log.error("【redis分布式鎖】解鎖異常, {}", e);
        }
    }

}      
public void orderProductMockDiffUser(String productId)
    {
        //加鎖
        long time=System.currentTimeMillis()+TIMEOUT;
        if(!redisLock.lock(productId,String.valueOf(time))){
            throw new SellException(ResultEnum.REDIS_LOCK_FAIL);
        }
        //1.查詢該商品庫存,為0則活動結束。
        int stockNum = stock.get(productId);
        if(stockNum == 0) {
            throw new SellException(100,"活動結束");
        }else {
            //2.下單(模拟不同使用者openid不同)
            orders.put(KeyUtil.genUniqueKey(),productId);
            //3.減庫存(模拟在記憶體(或redis)中減庫存)
            stockNum =stockNum-1;
            try {
                //4.模拟一些IO或其他業務操作
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            stock.put(productId,stockNum);
        }

        //解鎖
        redisLock.unlock(productId,String.valueOf(time));
    }      

繼續閱讀