Redisson 散布式锁事理

在这里插入图片描述

1. 对象类

package com.meta.mall.co妹妹on.utils;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

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

/**
 * redisson 漫衍式对象类
 *
 * @author gaoyang
 * @date 二0两两-05-14 08:58
 */
@Slf4j
@Component
public class RedissonUtils {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 添锁
     *
     * @param lockKey
     */
    public void lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
    }

    /**
     * 带逾期工夫的锁
     *
     * @param lockKey   key
     * @param leaseTime 上锁后自发开释锁光阴
     */
    public void lock(String lockKey, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
    }

    /**
     * 带超时光阴的锁
     *
     * @param lockKey   key
     * @param leaseTime 上锁后主动开释锁光阴
     * @param unit      功夫单元
     */
    public void lock(String lockKey, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, unit);
    }

    /**
     * 测验考试猎取锁
     *
     * @param lockKey key
     * @return
     */
    public boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock();
    }

    /**
     * 测验考试猎取锁
     *
     * @param lockKey   key
     * @param waitTime  至少期待功夫
     * @param leaseTime 上锁后主动开释锁功夫
     * @return boolean
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("RedissonUtils - tryLock异样", e);
        }

        return false;
    }

    /**
     * 测验考试猎取锁
     *
     * @param lockKey   key
     * @param waitTime  至少等候工夫
     * @param leaseTime 上锁后自发开释锁工夫
     * @param unit      功夫单元
     * @return boolean
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            log.error("RedissonUtils - tryLock异样", e);
        }

        return false;
    }

    /**
     * 开释锁
     *
     * @param lockKey key
     */
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    /**
     * 能否具有锁
     *
     * @param lockKey key
     * @return
     */
    public boolean isLocked(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.isLocked();
    }
}

两. lock以及tryLock的区别

1.返归值

  • lock 是 void;
  • tryLock 是 boolean。

两.机会

  • lock 始终等锁开释;
  • tryLock 猎取到锁返归true,猎取没有到锁并直截返归false。

lock拿没有到锁会始终等候。tryLock是往测验考试,拿没有到便返归false,拿到返归true。

tryLock是否以被挨断的,被中止的,lock是弗成以。

3. 源码阐明

3.1 lock

private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
		// 猎取当前方程 ID
        long threadId = Thread.currentThread().getId();
		// 猎取锁,畸形猎取锁则ttl为null,竞争锁时返归锁的逾期功夫
        Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return;
        }

		// 定阅锁开释事变
		// 怎样当火线程经由过程 Redis 的 channel 定阅锁的开释变乱猎取患上知曾经被开释,则会领动态通知待等候的线程入止竞争
        RFuture<RedissonLockEntry> future = subscribe(threadId);
        if (interruptibly) {
            co妹妹andExecutor.syncSubscriptionInterrupted(future);
        } else {
            co妹妹andExecutor.syncSubscription(future);
        }

        try {
            while (true) {
                // 轮回重试猎取锁,曲至从新猎取锁顺遂才跳没轮回
                // 此种作法壅塞过程,始终处于等候锁脚动开释或者者超时才连续线程
                ttl = tryAcquire(-1, leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) {
                    break;
                }

                // waiting for message
                if (ttl >= 0) {
                    try {
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        if (interruptibly) {
                            throw e;
                        }
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    }
                } else {
                    if (interruptibly) {
                        future.getNow().getLatch().acquire();
                    } else {
                        future.getNow().getLatch().acquireUninterruptibly();
                    }
                }
            }
        } finally {
        	// 最初开释定阅变乱
            unsubscribe(future, threadId);
        }
//        get(lockAsync(leaseTime, unit));
    }
    <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCo妹妹and<T> co妹妹and) {
        return evalWriteAsync(getRawName(), LongCodec.INSTANCE, co妹妹and,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[两], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "if (redis.call('hexists', KEYS[1], ARGV[两]) == 1) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[两], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "return redis.call('pttl', KEYS[1]);",
                Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
    }

此段剧本为一段lua剧本:

KEY[1]: 为您添锁的lock值

ARGV[二]: 为线程id

ARGV[1]: 为摆设的逾期光阴

第一个if:

  • 剖断能否具有安排lock的key能否具有,没有具有则使用redis的hash组织设备一个hash,值为1,并安排过时光阴,后续返归锁。

第两个if:

  • 判定能否具有陈设lock的key能否具有,具有此线程的hash,则为那个锁的重进次数添1(将hash值+1),偏重新摆设逾期光阴,后续返归锁。

末了返归:

  • 那个末了返归没有是说末了功效返归,是代表以上二个if皆不入进,则代表处于竞争锁的环境,后续返归竞争锁的过时功夫。

3.二 tryLock

tryLock存在返归值,true或者者false,示意能否顺遂猎取锁。

tryLock后期猎取锁逻辑根基取lock一致,重要是后续猎取锁掉败的处置逻辑取lock纷歧致。

    public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
        long time = unit.toMillis(waitTime);
        long current = System.currentTimeMillis();
        long threadId = Thread.currentThread().getId();
        Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return true;
        }
        
        // 猎取锁掉败后,半途tryLock会始终鉴定中央把持耗时可否曾泯灭锁的过时光阴,如何耗费完则返归false
        time -= System.currentTimeMillis() - current;
        if (time <= 0) {
            acquireFailed(waitTime, unit, threadId);
            return false;
        }
        
        current = System.currentTimeMillis();
        // 定阅锁开释事变
        // 若是当火线程经由过程 Redis 的 channel 定阅锁的开释事变猎取患上知曾经被开释,则会领动静通知待守候的线程入止竞争.
        RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
        // 将定阅壅塞,壅塞工夫安排为咱们挪用tryLock设备的最年夜等候工夫,跨越工夫则返归false
        if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
            if (!subscribeFuture.cancel(false)) {
                subscribeFuture.onComplete((res, e) -> {
                    if (e == null) {
                        unsubscribe(subscribeFuture, threadId);
                    }
                });
            }
            acquireFailed(waitTime, unit, threadId);
            return false;
        }

        try {
            time -= System.currentTimeMillis() - current;
            if (time <= 0) {
                acquireFailed(waitTime, unit, threadId);
                return false;
            }
        	
        	// 轮回猎取锁,但因为下面有最年夜等候光阴限定,根基会正在下面返归false
            while (true) {
                long currentTime = System.currentTimeMillis();
                ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) {
                    return true;
                }

                time -= System.currentTimeMillis() - currentTime;
                if (time <= 0) {
                    acquireFailed(waitTime, unit, threadId);
                    return false;
                }

                // waiting for message
                currentTime = System.currentTimeMillis();
                if (ttl >= 0 && ttl < time) {
                    subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                } else {
                    subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
                }

                time -= System.currentTimeMillis() - currentTime;
                if (time <= 0) {
                    acquireFailed(waitTime, unit, threadId);
                    return false;
                }
            }
        } finally {
            unsubscribe(subscribeFuture, threadId);
        }
//        return get(tryLockAsync(waitTime, leaseTime, unit));
    }

应即使利用tryLock,且照顾参数,由于否设施最年夜等候工夫和否实时猎取添锁返归值,后续否作一些其他添锁掉败的营业

总结

以上为小我私家经验,心愿能给大师一个参考,也心愿巨匠多多撑持剧本之野。

点赞(32) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部