Redis实现的简单分布式锁

直接看代码

package com.jd.medicine.base.common.spring.util;

import com.jd.jim.cli.Cluster;
import com.jd.medicine.base.common.logging.LogUtil;
import com.jd.medicine.base.common.util.StringUtil;
import org.slf4j.Logger;


import java.util.concurrent.TimeUnit;


/**
 * Redis实现的简单分布式锁
 * <p></p>
 * Created by  tanwei3 on 2016/08/02.
 */
public class RedisLock {

    private static final Logger LOG = LogUtil.getLogger(RedisLock.class);

    private Cluster jimClient;

    /**
     * 非原子操作
     * 尝试获取分布式锁,最多重试retryTimes次
     *
     * @param lockKey    锁缓存key
     * @param value      锁缓存key
     * @param expireTime  锁缓存有效期
     * @param retryTimes  失败重试次数
     * @param retrySleepMills  失败重试单隔毫秒
     * @return boolean true:成功  false:失败
     */
    @Deprecated
    public boolean tryLockWithRetry(String lockKey, String value, long expireTime, int retryTimes, long retrySleepMills) {
        if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(value) || expireTime <= 0 || retryTimes <= 0 || retrySleepMills <= 0){
            LOG.error("RedisLockImpl-->tryLockWithRetry| invalid parameter");
            return false;
        }
        while(retryTimes > 0){
            if(tryLock(lockKey, value, expireTime)){
                LOG.debug("RedisLockImpl-->tryLockWithRetry get lock success| retryTimes:{}", retryTimes);
                return true;
            }else{
                try {
                    TimeUnit.MILLISECONDS.sleep(retrySleepMills);
                } catch (InterruptedException e) {
                    LOG.error("RedisLockImpl-->tryLockWithRetry thread InterruptedException");
                }
            }

            retryTimes--;
        }

        //检测一下ttl是否是-1,表明key未设置过期时间
        try {
            Long ttlVal = jimClient.ttl(lockKey);
            if(ttlVal !=null && ttlVal == -1){
                jimClient.expire(lockKey, expireTime, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            LOG.error("RedisLockImpl-->tryLockWithRetry reset expire execute error! | lockKey={}", lockKey, e);
        }

        return false;
    }

    /**
     * 尝试获取分布式锁,最多重试retryTimes次
     * @param lockKey 锁缓存key
     * @param requestId 同一应用内的唯一ID,
     *                  使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
     *                  可以传任意非空和空串值
     *                  但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
     * @param expireTime 锁缓存有效期
     * @param timeUnit 锁缓存有效期时间单位
     * @param retryTimes 失败重试次数
     * @param retrySleepMills 失败重试单隔【毫秒】
     * @return
     */
    public boolean tryLockWithRetry(String lockKey, String requestId, long expireTime, TimeUnit timeUnit, int retryTimes, long retrySleepMills) {
        if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null
                || retryTimes <= 0 || retrySleepMills <= 0){
            LOG.error("RedisLockImpl --> tryLockWithRetry | invalid parameter");
            return false;
        }
        while(retryTimes > 0){
            if(tryLock(lockKey, requestId, expireTime, timeUnit)){
                LOG.debug("RedisLockImpl --> tryLockWithRetry get lock success | retryTimes:{}", retryTimes);
                return true;
            }else{
                try {
                    TimeUnit.MILLISECONDS.sleep(retrySleepMills);
                } catch (InterruptedException e) {
                    LOG.error("RedisLockImpl --> tryLockWithRetry thread InterruptedException");
                }
            }

            retryTimes--;
        }

        return false;
    }

    /**
     * 尝试获取分布式锁,最多重试retryTimes次 redis指令执行异常会抛出
     * @param lockKey 锁缓存key
     * @param requestId 同一应用内的唯一ID,
     *                  使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
     *                  可以传任意非空和空串值
     *                  但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
     * @param expireTime 锁缓存有效期
     * @param timeUnit 锁缓存有效期时间单位
     * @param retryTimes 失败重试次数
     * @param retrySleepMills 失败重试单隔【毫秒】
     * @return
     */
    public boolean tryLockWithRetryWithThrow(String lockKey, String requestId, long expireTime, TimeUnit timeUnit, int retryTimes, long retrySleepMills) {
        if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null
                || retryTimes <= 0 || retrySleepMills <= 0){
            LOG.error("RedisLock->tryLockWithRetryWithThrow|invalid parameter");
            return false;
        }
        while(retryTimes > 0){
            if(tryLockWithThrow(lockKey, requestId, expireTime, timeUnit)){
                LOG.debug("RedisLock->tryLockWithRetryWithThrow|get lock success|retryTimes:{}", retryTimes);
                return true;
            }else{
                try {
                    TimeUnit.MILLISECONDS.sleep(retrySleepMills);
                } catch (InterruptedException e) {
                    LOG.error("RedisLock->tryLockWithRetryWithThrow|thread InterruptedException");
                }
            }

            retryTimes--;
        }

        return false;
    }

    /**
     * 非原子操作
     * 尝试获取锁  如果锁可用   立即返回true,  否则返回false
     *  禁止使用永久锁,因为程序崩溃或者断电时有可能无法执行到finally,也就无法释放锁了
     * @param lockKey    锁缓存key
     * @param value      锁缓存key
     * @param expireTime  锁缓存有效期
     * @return 是否获得锁
     */
    @Deprecated
    public boolean tryLock(String lockKey, String value, long expireTime) {
        if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(value) || expireTime <= 0){
            LOG.error("RedisLockImpl-->tryLock | invalid parameter");
            return false;
        }
        try {

            LOG.trace("RedisLockImpl-->tryLock | lockKey:{}, value:{}, expireTime:{}",lockKey,value,expireTime);

            boolean successFlag = jimClient.setNX(lockKey, value);
            if (successFlag) {
                boolean expireFlag = jimClient.expire(lockKey, expireTime, TimeUnit.SECONDS);
                LOG.debug("RedisLockImpl-->tryLock set expire| expireFlag:{}, lockKey:{}, value:{}, expireTime:{}", expireFlag,lockKey,value,expireTime);
                return true;
            } else {
                // 存在锁,此时有可能前一个节点已经释放锁,或者其他节点获取了锁
                if(LOG.isDebugEnabled()){
                    //里面有jimClient.get操作,所以加一下判断
                    LOG.debug("RedisLockImpl-->tryLock lock conflict| lockKey:{}, value:{}",lockKey,jimClient.get(lockKey));
                }
                return false;
            }

        } catch (RuntimeException e) {
            LOG.error("RedisLockImpl-->tryLock execute error", e);
        }
        return false;
    }

    /**
     * 尝试获取锁  如果锁可用   立即返回true,  否则返回false
     * @param lockKey
     * @param requestId 同一应用内的唯一ID,
     *                  使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
     *                  可以传任意非空和空串值
     *                  但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
     * @param expireTime 超时时间
     * @param timeUnit 超时时间单位
     * @return
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime, TimeUnit timeUnit) {
        if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null){
            LOG.error("RedisLockImpl --> tryLock | invalid parameter");
            return false;
        }
        try {
            LOG.trace("RedisLockImpl --> tryLock | lockKey:{}, value:{}, expireTime:{}, timeUnit:{}",
                    lockKey, requestId, expireTime, timeUnit);
            // exist:false  不存在lockKey时才能操作 lockKey
            return jimClient.set(lockKey, requestId, expireTime, timeUnit, false);
        } catch (Exception e) {
            LOG.error("RedisLockImpl --> tryLock execute error", e);
        }
        return false;
    }

    /**
     * 尝试获取锁  如果锁可用   立即返回true,如果执行redis指令异常则抛出,否则返回false
     * @param lockKey
     * @param requestId 同一应用内的唯一ID,
     *                  使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
     *                  可以传任意非空和空串值
     *                  但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
     * @param expireTime 超时时间
     * @param timeUnit 超时时间单位
     * @return
     */
    public boolean tryLockWithThrow(String lockKey, String requestId, long expireTime, TimeUnit timeUnit) {
        if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null){
            LOG.error("RedisLock->tryLock|invalid parameter");
            return false;
        }
        try {
            LOG.trace("RedisLock->tryLock|lockKey:{}, value:{}, expireTime:{}, timeUnit:{}",
                    lockKey, requestId, expireTime, timeUnit);
            // exist:false  不存在lockKey时才能操作 lockKey
            return jimClient.set(lockKey, requestId, expireTime, timeUnit, false);
        } catch (Exception e) {
            LOG.error("RedisLock->tryLock|execute error", e);
            throw e;
        }
    }


    /**
     * 释放锁
     * @param lockKey
     */
    public void unLock(String lockKey) {
        if(StringUtil.isBlank(lockKey)){
            LOG.error("RedisLockImpl-->unLock| invalid parameter");
            return;
        }

        try {
            jimClient.del(lockKey);
            if(!jimClient.exists(lockKey)) {
                //删除成功
                LOG.debug("RedisLockImpl-->unLock release lock success| lockKey:{}", lockKey);
            } else {
                LOG.error("RedisLockImpl-->unLock release lock error| lockKey:{}", lockKey);
            }
        } catch (RuntimeException e) {
            LOG.error("RedisLockImpl-->unLock execute error ", e);
        }
    }

    public void setJimClient(Cluster jimClient) {
        this.jimClient = jimClient;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值