Redis实现分布式锁-源码记录


import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

/**
 * @author leitao.
 * @time: 2018/3/26  9:17
 * @version: 1.0
 * @description: 基于codis实现分布式锁
 **/
@Component
public class DistributedLock {

    private Jedis jedis;
    /**
     * 分布式锁key前缀
     */
    private final static String LOCK_KEY_PREFIX = "DL_";

    /**
     * 锁过期时间100毫秒
     */
    public final static Long TIMEOUT_100MS = 100L;

    public final static Long TIMEOUT_2000MS = 2000L;
    /**
     * 等待时间,30毫秒之后再重新获取锁
     */
    private final static Long WAIT_TIME = 30L;
    /**
     * 尝试获取锁次数
     */
    private final static int TRY_TIMES = 3;

    private static final Log LOG = LogFactory.getLog(DistributedLock.class);

    /**
     * 1. setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2.
     * 2. get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,
     * 则认为这个锁已经超时,可以允许别的请求重新获取,转向3.
     * 3. 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime.
     * 4. 判断currentExpireTime与oldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁,如果不相等,
     * 说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败.
     *
     * @param key
     * @return
     */
    public boolean lock(String key, Long timeout) {
        Long setnx = jedis.setnx(this.getDistributedLockKey(key), this.getExpireTime(timeout));
        if (1L == setnx) {
            //获取锁成功
            return true;
        }
        String oldExpireTimeStr = jedis.get(this.getDistributedLockKey(key));
        if (NumberUtils.isNumber(oldExpireTimeStr)) {
            Long oldExpireTime = Long.parseLong(oldExpireTimeStr);
            if (oldExpireTime < System.currentTimeMillis()) {
                String currentExpireTimeStr = jedis.getSet(this.getDistributedLockKey(key), this.getExpireTime(timeout));
                if (StringUtils.equals(currentExpireTimeStr, oldExpireTimeStr)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 若第一次没有获取到锁,则尝试直到获取到锁为止,做多尝试次数3次,间隔30ms尝试一次
     *
     * @param key
     * @return
     */
    public boolean tryLock(String key, Long timeout) {
        for (int i = 0; i < TRY_TIMES; i++) {
            Long setnx = jedis.setnx(this.getDistributedLockKey(key), this.getExpireTime(timeout));
            if (1L == setnx) {
                //获取锁成功
                return true;
            }
            String oldExpireTimeStr = jedis.get(this.getDistributedLockKey(key));
            if (NumberUtils.isNumber(oldExpireTimeStr)) {
                Long oldExpireTime = Long.parseLong(oldExpireTimeStr);
                if (oldExpireTime < System.currentTimeMillis()) {
                    String currentExpireTimeStr = jedis.getSet(this.getDistributedLockKey(key), this.getExpireTime(timeout));
                    if (StringUtils.equals(currentExpireTimeStr, oldExpireTimeStr)) {
                        return true;
                    }
                }
            }
            try {
                Thread.sleep(WAIT_TIME);
            } catch (InterruptedException e) {
                LOG.error(e.getMessage(), e);
            }
        }
        return false;
    }

    /**
     * 释放锁
     *
     * @param key
     */
    public void unLock(String key) {
        jedis.del(this.getDistributedLockKey(key));
    }

    /**
     * 获取分布式锁的key
     *
     * @param key
     * @return
     */
    private String getDistributedLockKey(String key) {
        return LOCK_KEY_PREFIX + key;
    }

    /**
     * 获取分布式锁过期时间
     *
     * @return
     */
    private String getExpireTime(Long timeout) {
        return System.currentTimeMillis() + timeout + "";
    }


}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值