RedissonClient中提供了好多种锁,还有其它很多实用的方法。Redisson是Redis官方推荐的Java版的Redis客户端。实现了对数据的增删改查等操作。Redisson实现了RedissonClient的接口。这里只介绍其中的锁。
org.redisson
redisson
3.10.7
重入锁可以通过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;
}
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