import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.xml.sax.SAXException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* Redisson 联锁(RedissonMultiLock)的使用
* 尝试获取多个锁,如果在指定时间获取不到全部指定的锁,则失败
* 注意 getLock 仅获取分布式锁的实例引用,可以多次获取,多次获取后的实例是相同的
* @author : lyn
* @date : 2022-04-24 21:45
*/
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedissonMultiLockTest {
@Autowired
private RedissonClient redissonClient;
ExecutorService executorService = Executors.newFixedThreadPool(2);
@After
public void after() {
// executorService.shutdown();
}
/**
* 测试 联锁(RedissonMultiLock)的使用
* 注意
* RedissonMultiLock 不支持isLocked()方法
* isLocked()方法 只是简单地检查锁是否处于被持有的状态
* isLockedSuccessfully 用于跟踪是否成功获取了锁,否则容易出现 java.util.concurrent.ExecutionException: java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id:
* 业务逻辑中unlock前最好判断是否当前线程获取到锁,否则容易出现上面的异常
*/
@SneakyThrows
@Test
public void testTryMultiLockFailure() {
// 线程1尝试获取锁,应该成功
Runnable task1 = () -> {
RLock lock1 = redissonClient.getLock("lock1");
RLock lock2 = redissonClient.getLock("lock2");
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2);
//用于跟踪是否成功获取了锁
boolean isLockedSuccessfully = false;
try {
// waitTime:等待获取锁的最长时间
// leaseTime:租约时间,如果当前线程成功获取到锁,那么锁将被持有的时间长度。这个时间过后,锁会自动释放
boolean hasLock = multiLock.tryLock(1L, 10L, TimeUnit.SECONDS);
if (hasLock) {
isLockedSuccessfully = true; // 标记成功获取锁
log.info("执行线程1的逻辑-开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("线程1在执行过程中被中断", e);
throw new RuntimeException(e);
}
log.info("执行线程1的逻辑-结束");
} else {
log.info("执行线程1获取锁失败");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//RedissonMultiLock 不支持isLocked()
if (isLockedSuccessfully) {
multiLock.unlock();
log.info("锁已释放");
}
}
};
// 线程2尝试获取锁,应该失败(由于线程1已经持有锁)
Runnable task2 = () -> {
RLock lock = redissonClient.getLock("lock1");
boolean isLockedSuccessfully = false;
try {
boolean hasLock = lock.tryLock(0L, 5L, TimeUnit.SECONDS);
if (hasLock) {
isLockedSuccessfully = true;
log.info("执行线程2的逻辑-开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("线程1在执行过程中被中断", e);
throw new RuntimeException(e);
}
log.info("执行线程2的逻辑-结束");
} else {
log.info("执行线程2获取锁失败");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
// if (isLockedSuccessfully && lock.isLocked()) {
lock.unlock();
// }
}
};
// 线程2尝试获取锁,应该失败(由于线程1已经持有锁)
Runnable task3 = () -> {
RLock lock = redissonClient.getLock("lock2");
boolean isLockedSuccessfully = false;
try {
boolean hasLock = lock.tryLock(0L, 5L, TimeUnit.SECONDS);
if (hasLock) {
isLockedSuccessfully = true;
log.info("执行线程3的逻辑-开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("线程1在执行过程中被中断", e);
throw new RuntimeException(e);
}
log.info("执行线程3的逻辑-结束");
} else {
log.info("执行线程3获取锁失败");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
if (isLockedSuccessfully && lock.isLocked()) {
lock.unlock();
}
}
};
Future<?> future1 = executorService.submit(task1);
//确保task1可以获取到锁
Thread.sleep(20);
Future<?> future2 = executorService.submit(task2);
Future<?> future3 = executorService.submit(task3);
future1.get();
future2.get();
future3.get();
}
/**
* 单元测试1的完善版,确保异步任务能执行完
*/
@SneakyThrows
@Test
public void test2() {
Runnable task1 = () -> {
try {
log.info("执行线程1的逻辑-开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("执行线程1的逻辑-结束");
} catch (Exception e) {
throw new RuntimeException(e);
}
};
Future<?> future1 = executorService.submit(task1);
// 等待异步任务完成
future1.get();
}
/**
* 以下测试程序存在(结束日志不会输出)
* 原因是 主线程未等异步任务执行完就退出了
*/
@Test
public void test1() {
Runnable task1 = () -> {
try {
log.info("执行线程1的逻辑-开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("执行线程1的逻辑-结束");
} catch (Exception e) {
throw new RuntimeException(e);
}
};
executorService.submit(task1);
}
}