package com.aswatson.cdc.test;

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.time.LocalDateTime;
import java.util.concurrent.*;

/**
 *  boolean success = lock.tryLock(0, 0, TimeUnit.SECONDS); // 表示尝试获取锁,等待0秒,持有锁0秒钟
 *  注意问题,存在的隐患: 虽然 tryLock(0, 0, TimeUnit.SECONDS)
 *
 *  首先1. 但实际锁的释放仍然会受到 Redisson 看门狗机制的影响。如果持有锁的线程未能在续约周期内续约锁的持有时间,那么锁可能会在超时后被自动释放。
 *  (默认是每隔 30 秒进行一次续约)来维持锁的有效性,避免因为持有锁的线程未能释放而造成锁的永久占用。或者自己unLock。
 *
 *  其次2. 确保你使用的 Redisson 版本与 Redis 版本兼容,并且不会因为版本问题导致锁的行为异常。目前测试用的是redis(2.7.17)、redisson(3.24.3)
 *
 *  其次3. 默认情况下,Redisson 的看门狗会定期发送续约请求给 Redis 服务器,以延长当前持有的锁的有效期。但是也有不会续约的可能性:
 *         Redis 连接中断、Redisson 配置问题、持有锁的线程崩溃、锁的最大持有时间到期。
 *
 *  其次4.  即使在业务逻辑中调用了阻塞操作(如 sleep),Redisson 也会在后台继续进行续约操作,以防止锁被意外释放。
 *
 */
public class TestRedissonLeaveTimeLock {

    public static void main(String[] args) throws Exception {

        Config config = new Config();
        config.useSingleServer().setAddress("redis://10.95.35.93:37495");
        RedissonClient redissonClient = Redisson.create(config);
        RLock lock = redissonClient.getLock("lockName");
        System.out.println("创建好了RedissonClient" + getName());

        int numThreads = 10;
        ExecutorService executor = Executors.newFixedThreadPool(numThreads);
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch doneLatch = new CountDownLatch(numThreads);

        // 尽管 for 循环看起来是按顺序逐个,但实际上每个任务会并发地在后台执行。
        // 这是因为每次调用 submit时,任务被提交给线程池,而线程池会根据可用的线程资源并发执行这些任务。
        for (int i = 0; i < numThreads; i++) {
            executor.submit(() -> {
                try {
                    startLatch.await(); // 等待主线程的启动信号

                    System.out.println("获取锁前的时间:"+ getName());
                    boolean success = lock.tryLock(0, 0, TimeUnit.SECONDS); // 尝试获取锁,等待0秒,持有锁0秒钟
                    System.out.println("获取锁后的时间:"+ getName());
                    if (success) {
                        System.out.println("拿到锁"+ getName());
                        // 模拟业务处理耗时 大于锁过期,可能导致非自己持有的锁被释放。
                        TimeUnit.SECONDS.sleep(20);
                    } else {
                        System.out.println("未能获取到锁,已放弃尝试" + getName());
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    doneLatch.countDown();// 每次减去1

                    // 判断当前线程是否持有锁
                    if (lock.isHeldByCurrentThread()) {
                        System.out.println("释放锁"+ getName());
                        lock.unlock();
                    }
                }
            });
        }

        System.out.println("主线程即将释放所有等待的线程...");
        startLatch.countDown(); // 释放定义的1条线程,开始并发执行
        doneLatch.await(); // 等待所有线程10条完成
        executor.shutdown();
        System.out.println("所有线程执行完成" + getName());
    }

    public static String getName() {
        return Thread.currentThread().getName() + "---" + LocalDateTime.now();
    }

}

标签: none

添加新评论