简易的Redis分布式锁

简易的Redis分布式锁

import cn.decentchina.kentucky.common.enums.ErrorCodeEnum;
import cn.decentchina.kentucky.common.exceptions.ErrorCodeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.Assert;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

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

/**
 * @Description: <br/>
 * 简易的Redis资源锁
 * <p>
 * <br/>
 * @Author: Qz1997
 * @create 2021/1/8 11:48
 */
@Slf4j
@Component
public class RedisLockUtil {
    
    
    /**
     * 锁固定前缀
     */
    private static final String REDIS_LOCK = "REDIS_LOCK_";
    /**
     * 默认提示信息
     */
    private static final String DEFAULT_MESSAGE = "未获得资源";
    /**
     * 锁过期时间 秒  默认: 5分钟
     */
    private static final Integer EXPIRE_TIME = 300;
    /**
     * 每次自旋睡眠时间 毫秒
     */
    private static final Integer SLEEP_TIME = 50;
    /**
     * 锁自旋次数
     */
    private static final Integer CYCLES = 10;

    @SuppressWarnings("all")
    @Resource(name = "redisTemplate")
    private ValueOperations<String, String> lockOperations;

    /**
     * 加锁
     *
     * @param key   加锁唯一标识
     * @param value 释放锁唯一标识
     */
    public void lock(String key, String value) {
    
    
        lock(key, value, EXPIRE_TIME, null);
    }

    /**
     * 加锁
     *
     * @param key     加锁唯一标识
     * @param value   释放锁唯一标识
     * @param message 获取不到锁提示信息
     */
    public void lock(String key, String value, String message) {
    
    
        lock(key, value, EXPIRE_TIME, message);
    }

    /**
     * 加锁
     *
     * @param key     加锁唯一标识
     * @param value   释放锁唯一标识
     * @param timeout 超时时间 单位:秒
     */
    public void lock(String key, String value, Integer timeout, String message) {
    
    
        Assert.isTrue(StringUtils.isNotBlank(key), "资源锁Key不能为空");
        Assert.isTrue(StringUtils.isNotBlank(value), "资源锁Value不能为空");
        // 自旋次数
        int cycles = CYCLES;
        //  尝试获取锁 当获取到锁 则直接返回 否则 循环尝试获取
        while (!tryLock(key, value, timeout)) {
    
    
            // 最多循环10次 当尝试了10次都没有获取到锁
            if (0 == (cycles--)) {
    
    
                log.error("尝试获取锁失败 key: {}, value: {}", key, value);
                throw new ErrorCodeException(ErrorCodeEnum.NO, StringUtils.isBlank(message) ? DEFAULT_MESSAGE : message);
            }
            try {
    
    
                TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
            } catch (Exception e) {
    
    
                log.error("尝试获取锁异常", e);
            }
        }
    }

    /**
     * 释放锁
     *
     * @param key   加锁唯一标识
     * @param value 释放锁唯一标识
     */
    public void unLock(String key, String value) {
    
    
        Assert.isTrue(StringUtils.isNotBlank(key), "资源锁Key不能为空");
        Assert.isTrue(StringUtils.isNotBlank(value), "资源锁Value不能为空");
        key = REDIS_LOCK + key;
        // 通过value判断是否是该锁 是则释放 不是则不释放 避免误删
        if (StringUtils.equals(value, lockOperations.get(key))) {
    
    
            lockOperations.getOperations().delete(key);
        }
    }

    /**
     * 尝试获取锁
     *
     * @param key     加锁唯一标识
     * @param value   释放锁唯一标识
     * @param timeout 超时时间 单位:秒
     * @return true: 加锁成功  false: 加锁失败
     */
    private boolean tryLock(String key, String value, Integer timeout) {
    
    
        Boolean result = lockOperations.setIfAbsent(REDIS_LOCK + key, value, timeout, TimeUnit.SECONDS);
        return result != null && result;
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_43939924/article/details/113047136