【Redisson】CountDownLatch 锁源码剖析

317 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

CountDownLatch 也是 Redis 分布式锁支持的一种,同步组件。

CountDownLatch 倒们栓:必须有满足数量的线程来获取锁,达到线程的数量之后,才会往下走; 否则,阻塞。

举个栗子:倒们栓设置 3个。

@Test
public void test8() throws InterruptedException {
​
    RCountDownLatch countDownLatch = redisson.getCountDownLatch("countDownLatch");
    countDownLatch.trySetCount(3);
​
    System.out.println(LocalDateTime.now()
                       + " : 线程[" + Thread.currentThread().getName() 
                       + "] 设置了必须有3个线程执行 countDown,进入等待中。。。");
    for (int i = 0; i < 3; ++i) {
        new Thread(() -> {
            try {
                System.out.println(LocalDateTime.now()
                                   + " : 线程[" + Thread.currentThread().getName() 
                                   + "] 在做一些操作,请耐心等待。。。");
                Thread.sleep(3000);
                RCountDownLatch localLatch = 
                    redisson.getCountDownLatch("countDownLatch");
                localLatch.countDown();
                System.out.println(LocalDateTime.now()
                                   + " : 线程[" + Thread.currentThread().getName() 
                                   + "] 执行 countDown 操作");
            } catch (Exception e) {
​
                e.printStackTrace();
            }
        }).start();
    }
​
    countDownLatch.await();
    System.out.println(LocalDateTime.now()
                       + " : 线程[" + Thread.currentThread().getName() 
                       + "] 收到通知,有3个线程都执行了 countDown 操作,可以继续往下走");
}

输出结果:

2022-06-21T17:36:56.156 : 线程[main] 设置了必须有3个线程执行 countDown,进入等待中。。。
2022-06-21T17:36:56.158 : 线程[Thread-3] 在做一些操作,请耐心等待。。。
2022-06-21T17:36:56.158 : 线程[Thread-4] 在做一些操作,请耐心等待。。。
2022-06-21T17:36:56.158 : 线程[Thread-5] 在做一些操作,请耐心等待。。。
2022-06-21T17:36:59.203 : 线程[Thread-4] 执行 countDown 操作
2022-06-21T17:36:59.216 : 线程[Thread-5] 执行 countDown 操作
2022-06-21T17:36:59.216 : 线程[Thread-3] 执行 countDown 操作
2022-06-21T17:36:59.270 : 线程[main] 收到通知,有3个线程都执行了 countDown 操作,可以继续往下走
  1. 设置倒们栓数值源码定位:RedissonCountDownLatch#trySetCountAsync

对应参数如下:

  • KEYS[1]:锁的名称 "countDownLatch"
  • KEYS[2]:通道名称,redisson_countdownlatch__channel__{锁名}
  • ARGV[1]CountDownLatchPubSub.NEW_COUNT_MESSAGE
  • ARGV[2]:倒们栓数量,需要输入的
if redis.call('exists', KEYS[1]) == 0 then -- 不存在对应的 锁 key
    redis.call('set', KEYS[1], ARGV[2]); 
    redis.call('publish', KEYS[2], ARGV[1]); 
return 1  -- 返回成功
else 
    return 0  -- 返回失败
end
  1. 等待,源码定位:RedissonCountDownLatch#await
public void await() throws InterruptedException {
    RFuture<RedissonCountDownLatchEntry> future = subscribe();
    try {
        commandExecutor.syncSubscription(future);
        // 无限循环
        while (getCount() > 0) { // 当前倒们栓的数量 > 0 
            // waiting for open state
            RedissonCountDownLatchEntry entry = getEntry();
            if (entry != null) {
                entry.getLatch().await();  // 等待某一段时间
            }
        }
    } finally {
        unsubscribe(future);
    }
}
  1. 减少倒们栓数值,源码定位:RedissonCountDownLatch#countDownAsync

对应参数如下:

  • KEYS[1]:锁的名称 "countDownLatch"
  • KEYS[2]:通道名称,redisson_countdownlatch__channel__{锁名}
  • ARGV[1]CountDownLatchPubSub.NEW_COUNT_MESSAGE
local v = redis.call('decr', KEYS[1]);          -- 自减 -1
if v <= 0 then redis.call('del', KEYS[1]) end;  -- 倒们栓数量 <=0,删除对应 key
if v == 0 then redis.call('publish', KEYS[2], ARGV[1]) end;