redis分布式锁实现

一. redis单机部署时原生实现的分布式锁

满足:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。
  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  3. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

缺陷:

  1. 不可重入
  2. 不能满足多个Redis实例的情况
  3. 获取锁没有循环获取,一次不成功就返回了。
/**
 * @Author: zqf
 * @Description: 同一时间只能有一个线程可以获取到锁,其他没有获取到锁的线程直接丢掉时(比如redis避免缓存击穿时,数据失效异步更  新数据)
 * @Date 2021/8/25 10:08
 */
public class DistributedLock {

    /**
     * 释放锁lua脚本,原子操作:lua脚本是作为一个整体执行的,所以中间不会被其他命令插入。
     */
    private final static String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    private static final Long RELEASE_SUCCESS = 1L;

    /**
     *
     * @param lockId 锁
     * @param requestId 请求标识
     * @param seconds 过期时间
     * @return
     */
    public static boolean getLock(RedisTemplate redisTemplate, String lockId, String requestId, long seconds) {
        Boolean success = redisTemplate.opsForValue().setIfAbsent(lockId, requestId, seconds, TimeUnit.SECONDS);
        return success != null && success;
    }

    /**
     * 释放锁
     * @param redisTemplate
     * @param lockId 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseLock(RedisTemplate redisTemplate, String lockId, String requestId) {
        // 指定 lua 脚本,并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT,Long.class);
        // 参数一:redisScript,参数二:key列表,参数三:arg(可多个)
        Long result = (Long) redisTemplate.execute(redisScript, Collections.singletonList(lockId), requestId);
        return RELEASE_SUCCESS.equals(result);
    }
}

二.jedis实现分布式锁

public class RedisLockUtils {

    private static final String LOCK_KEY = "HT:LOCK:";

    /**
     * 分布式锁
     *
     * @param jedis
     * @param lockName
     * @param acquireTimeout
     * @param lockTimeout
     * @return
     */
    public static boolean lock(Jedis jedis, String lockName, long acquireTimeout, long lockTimeout) {
        String realLockName = LOCK_KEY + lockName;
        String theadId = String.valueOf(Thread.currentThread().getId());
        if (acquireTimeout == 0) {
            //如未设置锁获取超时时间默认为10秒
            acquireTimeout = 10000l;
        }
        if (lockTimeout == 0) {
            //如未设置锁超时时间默认未60秒
            lockTimeout = 60000l;
        }
        long lockTimeoutMin = (lockTimeout / 1000l);
        long acquireEndTime = System.currentTimeMillis() + acquireTimeout;
        //防止未设置时间产生死锁,当 key 不存在时,返回 -2 ,当 key 存在但没有设置剩余生存时间时,返回 -1 。
        // 在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回 -1 。
        if (jedis.ttl(realLockName) == -1) {
            jedis.expire(realLockName, lockTimeoutMin);
        }
        while (System.currentTimeMillis() < acquireEndTime) {
            //加锁成功
            if (theadId.equals(jedis.get(realLockName)) || jedis.setnx(realLockName, theadId) == 1) {
                log.info("线程{}获取锁成功", theadId);
                jedis.expire(realLockName, lockTimeoutMin);
                return true;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 当前线程 释放锁
     *
     * @param jedis
     * @param lockName
     * @return
     */
    public static boolean releaseLock(Jedis jedis, String lockName) {
        String realLockName = LOCK_KEY + lockName;
        String theadId = String.valueOf(Thread.currentThread().getId());
        boolean isRelease = false;
        while (true) {
            //jedis事务操作   监控realLockName  如果其中值发生变化tx.exec事务不会提交(注jedis中的事务不具备原子性,其中一个命令有错误,另外的仍然执行)
            jedis.watch(realLockName);
            if (theadId.equals(jedis.get(realLockName))) {
                Transaction tx = jedis.multi();
                tx.del(realLockName);
                if (tx.exec().isEmpty()) {
                    continue;
                }
                log.info("线程{}释放锁成功", theadId);
                isRelease = true;
            }
            //取消监控
            jedis.unwatch();
            break;
        }
        return isRelease;
    }
}



三.使用Redisson实现分布式锁
参考:https://www.jianshu.com/p/47fd7f86c848,
https://www.cnblogs.com/qdhxhz/p/11059200.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值