分布式锁实现方案3、基于Redis的SET操作实现的分布式锁

在我的上一篇文章中,关于redis分布式锁的写法,释放锁还有些缺陷,细节见评论部分,本文进一步做了完善。
分布式锁实现方案2、基于Redis的SET操作实现的分布式锁


package com.alioo.common.lock;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

/**
 * <pre>
 * 基于Redis的SET操作实现的分布式锁
 * </pre>
 *
 * @author lzc.java@icloud.com
 */
@Slf4j
public class RedisDistributedLock {

    private JedisFacade redis;

    // 锁key
    private String lockKey;

    // 锁value
    private String lockVal;

    // 是否获得锁
    private boolean locked;

    private static final String RELEASE_LOCK_LUA_SCRIPT
        = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    /**
     * @param redis
     * @param lockKey lockKey
     */
    public RedisDistributedLock(JedisFacade redis, String lockKey) {
        this.redis = redis;
        this.lockKey = lockKey;
    }

    /**
     * 阻塞式获取锁 ,不过有超时时间waitTime,超过了waitTime还未获取到锁将直接返回false
     *
     * @param lockTime 获得锁之后过期时间
     * @param waitTime 获得锁最多等待时间
     * @param unit
     * @return
     */
    protected boolean lock(long lockTime, long waitTime, TimeUnit unit) {
        try {
            // 超时控制 的时间可以从本地获取, 因为这个和锁超时没有关系, 只是一段时间区间的控制
            long start = System.currentTimeMillis();
            long milliTimeout = unit.toMillis(waitTime);

            while (System.currentTimeMillis() - start < milliTimeout) {

                boolean flag = tryLock(lockTime, unit);
                if (flag) {
                    return true;
                }

                Thread.sleep(10L);
            }
        } catch (InterruptedException e) {
            return false;
        }
        return false;
    }

    /**
     * 非阻塞,立即返回是否获取到锁
     *
     * @param lockTime 获得锁之后过期时间
     * @param unit
     * @return
     */
    public boolean tryLock(long lockTime, TimeUnit unit) {
        long milliLockTime = unit.toMillis(lockTime);

        //锁过期时间
        long lockExpireTime = System.currentTimeMillis() + milliLockTime;
        lockVal = String.valueOf(lockExpireTime);

        String ret = redis.set(lockKey, lockVal, "NX", "PX", milliLockTime);
        if ("OK".equals(ret)) {
            // 获取到锁, 设置相关标识
            locked = true;
            return true;
        }
        return false;
    }

    public boolean isLocked() {
        return locked;
    }

    /**
     * 释放锁
     */
    public void unlock() {
        if (!locked) {
            return;
        }

        // 当前线程加的锁也许已经过期自动释放了,然后又被其它线程加锁了,所以在释放锁的时候进一步根据lockVal再check下
        Object ret = redis.eval(RELEASE_LOCK_LUA_SCRIPT, Collections.singletonList(lockKey),
            Collections.singletonList(lockVal));
        log.info("释放锁lockKey:{},lockVal:{},ret:{}", lockKey, lockVal, ret);

    }

}

使用示例


        String lockKey = String.format("%s:%s:%s:%s", REDIS_LOCK_PREFIX, "biz_type", "sub_sub_type", userId);
        RedisDistributedLock lock = new RedisDistributedLock(jedisFacade, lockKey);
        Boolean result;
        try {
            boolean locked = lock.tryLock(5000, TimeUnit.MILLISECONDS);
            if (!locked) {
                throw new ServiceException(SysErrorCode.REDIS_LOCK_ERROR);
            }
            
            // do your service
			doYourService()

        } finally {
            lock.unlock();
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值