Redis实现分布式锁

只谈情不闲聊 提交于 2021-01-01 07:10:17

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * Redis分布式锁
 */
@Component
@Slf4j
public class RedisLock {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static final long EXPIRE = 30 * 1000L; // 失效时间

    private static final long TIMEOUT = 10 * 1000L;  // 等待时间

    /**
     * 分布式加锁
     * @param key
     * @param value requestTime + expireTime
     * @return
     */
    public boolean secKilllock(String key,String value){
        if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//对应setnx命令
            log.info("[Redis分布式锁 secKilllock()]获取锁成功 kye:{},value:{}",key,value);
            return true;
        }
        //判断锁超时
        String currentValue = stringRedisTemplate.opsForValue().get(key);
        log.info("[Redis分布式锁 secKilllock()]获取锁成功 kye:{},value:{} currentValue:{}",key,value, currentValue);
        //如果锁过期
        if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){//currentValue不为空且小于当前时间
            //获取上一个锁的时间value
            String oldValue = stringRedisTemplate.opsForValue().getAndSet(key,value);//对应getset,如果key存在
            log.info("[Redis分布式锁 secKilllock()]获取锁成功 kye:{},value:{} currentValue:{} oldValue:{}",key,value, currentValue, oldValue);
            if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue) ){
                //oldValue不为空且oldValue等于currentValue,也就是校验是不是上个时间戳,也是防止并发
                return true;
            }
        }
        log.info("[Redis分布式锁 secKilllock()]获取锁失败 kye:{},value:{}",key,value);
        return false;
    }


    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key,String value){
        try {
            String currentValue = stringRedisTemplate.opsForValue().get(key);
            log.info("[Redis分布式锁 unlock] 解锁 key:{} value:{} currentValue:{}", key, value, currentValue);
            if(!StringUtils.isEmpty(currentValue) && currentValue.equals(value) ){
                log.info("[Redis分布式锁 unlock] 解锁 key:{} value:{} currentValue:{} 删除KEY开始", key, value, currentValue);
                stringRedisTemplate.opsForValue().getOperations().delete(key);//删除key
            }
        } catch (Exception e) {
            log.error("[Redis分布式锁 unlock] 解锁出现异常了,{}", e);
        }
    }

    /**
     * 加锁 获取锁失败会重试获取锁
     * @param key
     * @param value
     * @return
     */
    public boolean lock(String key,String value){
        log.info("获取锁 key:{},value:{}",key,value);
        //请求锁时间
        long requestTime = System.currentTimeMillis();
        while (true) {
            //等待锁时间
            long watiTime = System.currentTimeMillis() - requestTime;
            //如果等待锁时间超过10s,加锁失败
            if(watiTime > TIMEOUT){
                log.info("等待锁超时 kye:{},value:{}",key,value);
                return false;
            }

            if(stringRedisTemplate.opsForValue().setIfAbsent(key,String.valueOf(System.currentTimeMillis()))){
                //获取锁成功
                log.info("[Redis分布式锁]获取锁成功 kye:{},value:{}",key,value);
                //设置超时时间,防止解锁失败,导致死锁
                stringRedisTemplate.expire(key, EXPIRE, TimeUnit.MILLISECONDS);
                return true;
            }

            String valueTime = stringRedisTemplate.opsForValue().get(key);
            if(! StringUtils.isEmpty(valueTime) && System.currentTimeMillis() - Long.parseLong(valueTime) > EXPIRE){
                //加锁时间超过过期时间,删除key,防止死锁
                log.info("[Redis分布式锁]锁超时, key:{}, value:{}", key, value);
                try{
                    stringRedisTemplate.opsForValue().getOperations().delete(key);
                }catch (Exception e){
                    log.info("[Redis分布式锁]删除锁异常 key:{}, value:{}", key, value);
                    e.printStackTrace();
                }
                return false;
            }

            //获取锁失败,等待20毫秒继续请求
            try {
                log.info("[Redis分布式锁]获取锁失败等待20 nanoSeconds key:{},value:{}",key,value);
                TimeUnit.NANOSECONDS.sleep(20);
            } catch (InterruptedException e) {
                log.info("等待20 nanoSeconds 异常 key:{},value:{}",key,value);
                e.printStackTrace();
            }

        }
    }
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!