| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 |
- package com.izouma.nineth.service;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.stereotype.Component;
- @Component
- @Slf4j
- public class RedisLock {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
- /**
- * 加锁
- *
- * @param key productId - 商品的唯一标志
- * @param value 当前时间+超时时间 也就是时间戳
- * @return
- */
- public boolean lock(String key, String value) {
- if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {//对应setnx命令
- //可以成功设置,也就是key不存在
- return true;
- }
- //判断锁超时 - 防止原来的操作异常,没有运行解锁操作 防止死锁
- String currentValue = stringRedisTemplate.opsForValue().get(key);
- //如果锁过期
- if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {//currentValue不为空且小于当前时间
- //获取上一个锁的时间value
- String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);//对应getset,如果key存在
- //假设两个线程同时进来这里,因为key被占用了,而且锁过期了。获取的值currentValue=A(get取的旧的值肯定是一样的),两个线程的value都是B,key都是K.锁时间已经过期了。
- //而这里面的getAndSet一次只会一个执行,也就是一个执行之后,上一个的value已经变成了B。只有一个线程获取的上一个值会是A,另一个线程拿到的值是B。
- if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
- //oldValue不为空且oldValue等于currentValue,也就是校验是不是上个对应的商品时间戳,也是防止并发
- return true;
- }
- }
- return false;
- }
- /**
- * 解锁
- *
- * @param key
- * @param value
- */
- public void unlock(String key, String value) {
- try {
- String currentValue = stringRedisTemplate.opsForValue().get(key);
- if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
- stringRedisTemplate.opsForValue().getOperations().delete(key);//删除key
- }
- } catch (Exception e) {
- log.error("[Redis分布式锁] 解锁出现异常了,{}", e);
- }
- }
- }
|