redis分布式锁-Redisson(RedissonClient)

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

依赖


      org.redisson
      redisson
      3.10.7

重入锁 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 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 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 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 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 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 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

 

 

你可能感兴趣的:(java,redis,java)