redis分布式锁-Redisson(RedissonClient)

RedissonClient中提供了好多种锁,还有其它很多实用的方法。Redisson是Redis官方推荐的Java版的Redis客户端。实现了对数据的增删改查等操作。Redisson实现了RedissonClient的接口。这里只介绍其中的锁。

依赖

<dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.10.7</version>
</dependency>


重入锁 RedissonLock
重入锁可以通过Redisson的getLock方法获取

@Override
public RLock getLock(String name) {
    return new RedissonLock(connectionManager.getCommandExecutor(), name);
}
/**
     * 获取锁-同一个线程可重入
     * @param lockKey  锁的名称
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit  时间的单位
     * @return 获取锁的结果
     */
    public Boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 1. 最常见的使用方法
            //lock.lock();
            // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
            //lock.lock(10, TimeUnit.SECONDS);
            boolean locked = lock.tryLock(waitTime, leaseTime, unit);
            if (locked) lockKeys.add(lockKey);
            return locked;
        } catch (InterruptedException e) {
            System.out.println(String.format("尝试获取锁%s失败", lockKey));
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }
 
    /**
     * 解锁 - 重入的方式,所以同一个线程加了几次锁就要释放几次锁
     * @param lockKey 锁的值
     */
    public boolean unLock(String lockKey) {
        try {
            RLock lock = redissonClient.getLock(lockKey);
            if (null != lock && lock.isHeldByCurrentThread()) { //判断锁是否存在,和是否当前线程加的锁。
                lock.unlock();
                return lockKeys.remove(lockKey);
            }
        } catch (Exception e) {
            System.out.println(String.format("解锁锁%s失败", lockKey));
            e.printStackTrace();
        }
        return false;
    }


重入锁的异步执行方式

/**
     * 异步获取锁-同一个线程可重入
     * @param lockKey  锁的名称
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit  时间的单位
     * @return 获取锁的结果
     */
    public Boolean tryLockAsync(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 1. 最常见的使用方法
            //lock.lockAsync();
            // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
            //lock.lockAsync(10, TimeUnit.SECONDS);
            RFuture<Boolean> locked = lock.tryLockAsync(waitTime, leaseTime, unit);
            if (locked.get()) lockKeys.add(lockKey);
            return locked.get();
        } catch (InterruptedException | ExecutionException e) {
            System.out.println(String.format("尝试获取锁%s失败", lockKey));
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }
 
    /**
     * 解锁 - 重入的方式,所以同一个线程加了几次锁就要释放几次锁
     * @param lockKey 锁的值
     */
    public boolean unAsyncLock(String lockKey) {
        try {
            RLock lock = redissonClient.getLock(lockKey);
            if (null != lock && lock.isHeldByCurrentThread()) { //判断锁是否存在,和是否当前线程加的锁。
                RFuture<Void> future = lock.unlockAsync();
                if(future.await(5 * 1000) && future.isSuccess()) {
                    return lockKeys.remove(lockKey);
                }
            }
        } catch (Exception e) {
            System.out.println(String.format("解锁%s失败", lockKey));
            e.printStackTrace();
        }
        return false;
    }


公平锁:
改公平锁是可重入的,在提供了自动过期解锁功能的同时,保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。同时也提供了异步的方式。实现方式参照上一个锁的实现。

@Override
public RLock getFairLock(String name) {
    return new RedissonFairLock(connectionManager.getCommandExecutor(), name);
}


/**
     * 公平锁
     *
     * @param lockKey 锁的名称
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit 时间的单位
     *
     * @return 获取锁的结果
     */
    public Boolean tryFairLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getFairLock(lockKey);
        try {
            // 1. 最常见的使用方法
            //lock.tryLock();
            // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
            //lock.tryLock(10, TimeUnit.SECONDS);
            boolean locked = lock.tryLock(waitTime, leaseTime, unit);
            if (locked)
                lockKeys.add(lockKey);
            return locked;
            /* 异步实现方式
            lock.lockAsync();
            lock.lockAsync(10, TimeUnit.SECONDS);
            RFuture<Boolean> locked = lock.tryLockAsync(waitTime, leaseTime, unit);
            if (locked.get()) lockKeys.add(lockKey);
            return locked.get();*/
        } catch (InterruptedException e) {
            System.out.println(String.format("尝试获取锁%s失败", lockKey));
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }
 
    /**
     * 解锁 - 重入的方式,所以同一个线程加了几次锁就要释放几次锁
     *
     * @param lockKey 锁的值
     */
    public boolean unFairLock(String lockKey) {
        try {
            RLock lock = redissonClient.getFairLock(lockKey);
            if (null != lock && lock.isHeldByCurrentThread()) { //判断锁是否存在,和是否当前线程加的锁。
                lock.unlock();
                return lockKeys.remove(lockKey);
 
                //异步方式删除锁
                /*RFuture<Void> future = lock.unlockAsync();
                if (future.await(5 * 1000) && future.isSuccess()) {
                    return lockKeys.remove(lockKey);
                }*/
            }
        } catch (Exception e) {
            System.out.println(String.format("解锁%s失败", lockKey));
            e.printStackTrace();
        }
        return false;
    }


联锁(MultiLock)
Redisson的RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例。需要注意的是,在锁的释放时,可以单独释放Redisson添加的锁,其他锁不会释放依旧存在。

/**
     * 连锁-只有所有的RedissonClient都锁成功才算成功
     *
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit 时间的单位
     *
     * @return 获取锁的结果
     */
    public Boolean tryMultiLock(RedissonClient redisson1,RedissonClient redisson2, long waitTime, long leaseTime, TimeUnit unit){
        RLock lock1 = redisson1.getLock("zhong:test:lock1");
        RLock lock2 = redisson2.getLock("zhong:test:lock2");
        RLock lock = redissonClient.getMultiLock(lock1, lock2);
        try {
            // 同时加锁:lock1 lock2 lock3, 所有的锁都上锁成功才算成功。
            lock.lock();
            // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
            boolean res = lock.tryLock(waitTime, unit);
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return false;
    }
    /**
     * 连锁 - 需要遵循谁加的锁设计释放锁,可以单独释放自己加的锁
     *
     */
    public boolean unMultiLock(RedissonClient client ,RedissonClient client1) {
        try {
            List<RLock> locks = new ArrayList<>();
            locks.add(client.getLock("zhong:test:lock1"));
            locks.add(client1.getLock("zhong:test:lock2"));
            RedissonMultiLock lock = new RedissonMultiLock(locks.toArray(new RLock[0]));
            lock.unlock();
 
            //异步方式删除锁
            /*RFuture<Void> future = lock.unlockAsync();
            if (future.await(5 * 1000) && future.isSuccess()) {
                return lockKeys.remove(lockKey);
            }*/
        } catch (Exception e) {
            System.out.println(String.format("解锁失败"));
            e.printStackTrace();
            return false;
        }
        return true;
    }


 RedissonClient还提供了红锁,读写锁等。

        在实际应用中我们最常用的分布式锁一般都是设置定时过期的。这样的锁在实际应用中存在一个问题就是服务宕机或重启这个锁在redis上是一直存在的。一旦重启就可能会导致所有线程无法获取到锁。解决办法就是在加锁的时候将锁记录到Set里面。释放锁的时候将记录Set中的锁删除,在服务停止之前就就根据set记录里面的锁先将欧锁释放。这样就能保证重启后能获取到锁。实现方式参考DisposableBean的实现方式。

https://www.javadoc.io/doc/org.redisson/redisson/3.11.6/org/redisson/api/RLockAsync.html

https://www.cnblogs.com/cjsblog/p/9831423.html

https://blog.csdn.net/clypm/article/details/80598074
————————————————
版权声明:本文为CSDN博主「飘零未归人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34484062/article/details/108517668

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Slf4j @Aspect @Component public class DistributedLockAspect { private final ExpressionParser parser = new SpelExpressionParser(); private final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); @Autowired private RedissonClient redissonClient; @Around("@annotation(distributedLock)") public Object lock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable { // 使用spel解析注解中定义的key String keyValue = parseKeyValue(joinPoint, distributedLock.value()); // 拼接出名称 String lockName = connectLockName(distributedLock.keyPrefix(), keyValue, distributedLock.separator()); // 获取 RLock lock = getLock(distributedLock.lockModel(), lockName); try { if (lock.tryLock(distributedLock.waitLockMSec(), distributedLock.lockExpireMSec(), TimeUnit.MILLISECONDS)) { log.info("获取:{}", lockName); return joinPoint.proceed(); } throw new LockException(distributedLock.message()); } finally { lock.unlock(); } } private RLock getLock(DistributedLock.LockModel lockModel, String lockName) { switch (lockModel) { case FAIR: //公平 return redissonClient.getFairLock(lockName); case READ: //读之前加读,读的作用就是等待该lockkey释放写以后再读 return redissonClient.getReadWriteLock(lockName).readLock(); case WRITE: //写之前加写,写成功,读只能等待 return redissonClient.getReadWriteLock(lockName).writeLock(); case REENTRANT: default: //可重入 return redissonClient.getLock(lockName); } } private String connectLockName(String prefix, String key, String separator) { if (StringUtils.isNotBlank(prefix)) { return prefix + separator + key; } return key; } private String parseKeyValue(ProceedingJoinPoint joinPoints, String elExpr) { MethodSignature methodSignature = (MethodSignature) joinPoints.getSignature(); Method method = methodSignature.getMethod(); //获取方法的参数值 Object[] args = joinPoints.getArgs(); EvaluationContext context = new StandardEvaluationContext(); String[] params = discoverer.getParameterNames(method); if (params != null) { for (int i = 0; i < params.length; i++) { context.setVariable(params[i], args[i]); } } //根据spel表达式获取值 Expression expression = parser.parseExpression(elExpr); Object value = expression.getValue(context); if (value != null) { return value.toString(); } return "defaultLockKey"; } }解释一下这段代码
03-23

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值