Redisson 联锁(RedissonMultiLock)的使用

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);
    }


}
posted @ 2024-06-18 16:39  进击的小蔡鸟  阅读(79)  评论(0编辑  收藏  举报