10.Redisson源碼-CountDownLatch源碼剖析

一、CountDownLatch基本原理

  1. countDownLatch最基本的原理其實(shí)就是,現(xiàn)在有4個(gè)客戶端,分別是A、B、C、D,客戶端A進(jìn)行加鎖后,設(shè)置三個(gè)線程來(lái)獲取鎖,那么,必須讓接下來(lái)的三個(gè)客戶端BCD都獲取鎖成功后,客戶端A的邏輯才會(huì)繼續(xù)向下走
  2. 如果說(shuō),指定3個(gè)客戶端獲取鎖,獲取鎖的客戶端數(shù)量沒(méi)有到達(dá)3的話,客戶端A是不會(huì)邏輯是不會(huì)向下走的,會(huì)被阻塞住

源碼

代碼片段一、demo

public static void main(String[] args) throws Exception {
        Config config = new Config();
        config.useClusterServers()
                .addNodeAddress("redis://192.168.0.107:7001")
                .addNodeAddress("redis://192.168.0.107:7002")
                .addNodeAddress("redis://192.168.0.110:7003")
                .addNodeAddress("redis://192.168.0.110:7004")
                .addNodeAddress("redis://192.168.0.111:7005")
                .addNodeAddress("redis://192.168.0.111:7006");

        final RedissonClient redisson = Redisson.create(config);

        RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
        // 這里會(huì)設(shè)置幾個(gè)客戶端來(lái)獲取鎖成功的數(shù)量,代碼片段二、
        latch.trySetCount(3);
        System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]設(shè)置了必須有3個(gè)線程執(zhí)行countDown,進(jìn)入等待中。。。");

        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]在做一些操作,請(qǐng)耐心等待。。。。。。");
                        Thread.sleep(3000);
                        RCountDownLatch localLatch = redisson.getCountDownLatch("anyCountDownLatch”);
                        // 代碼片段三、
                        localLatch.countDown();
                        System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]執(zhí)行countDown操作");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        // 代碼片段四、
        latch.await();
        System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]收到通知,有3個(gè)線程都執(zhí)行了countDown操作,可以繼續(xù)往下走");

    }

代碼片段二、RedissonCountDownLatch

  1. 參數(shù):
    KEYS[1]= “anyCountDownLatch”
    ARGV[2] = 3,其實(shí)就是count參數(shù)的值
@Override
public boolean trySetCount(long count) {
    // 這里設(shè)置的客戶端獲取鎖的個(gè)數(shù)為3
    return get(trySetCountAsync(count));
}

// count = 3
@Override
public RFuture<Boolean> trySetCountAsync(long count) {
    return commandExecutor.evalWriteAsync(getName(), 
    LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            //1.exists anyCountDownLatch 客戶端A進(jìn)來(lái)不存在,
            // 進(jìn)入if邏輯
            "if redis.call('exists', KEYS[1]) == 0 then “
                //1.set anyCountDownLatch 3
                + "redis.call('set', KEYS[1], ARGV[2]); "
                + "redis.call('publish', KEYS[2], ARGV[1]); “   
                //1.返回1代表成功
                + "return 1 "
            + "else "
                + "return 0 "
            + "end",
            Arrays.<Object>asList(getName(), getChannelName()),
             newCountMessage, count);
}

代碼片段三、RedissonCountDownLatch

參數(shù)
KEYS[1] = “anyCountDownLatch”


@Override
public void countDown() {
    get(countDownAsync());
}


@Override
public RFuture<Void> countDownAsync() {
    return commandExecutor.evalWriteAsync(getName(), 
    LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                    //1.decr anyCountDownLatch ,其實(shí)就是anyCountDownLatch這KEY對(duì)應(yīng)的值,
                    // 第一次是3,減去1,變成2,
                    // 這樣的話,后面還允許2個(gè)客戶端獲取鎖
                    "local v = redis.call('decr', KEYS[1]);” +
                    //1.如果v,第一次執(zhí)行后為2小于等于0的話,就直接刪除key,所以可以看到,
                    // 當(dāng)執(zhí)行到第三個(gè)客戶端的時(shí)候,這里才會(huì)成立
                    "if v <= 0 then redis.call('del', KEYS[1]) end;" +
                    "if v == 0 then redis.call('publish', KEYS[2], ARGV[1]) end;",
                Arrays.<Object>asList(getName(), getChannelName()), zeroCountMessage);
}

代碼片段四、RedissonCountDownLatch


public void await() throws InterruptedException {
    RFuture<RedissonCountDownLatchEntry> future = subscribe();
    try {
        commandExecutor.syncSubscription(future);

        // 這里就是說(shuō),如果我們業(yè)務(wù)邏輯里設(shè)置的成功獲取鎖的客戶端為3,如果三個(gè)客戶端都已經(jīng)成功獲取鎖,
        //那么KEY就不存在了,如代碼片段三中的分析,而如果獲取鎖的客戶端沒(méi)有達(dá)到3的話,這里其實(shí)就會(huì)進(jìn)入到一個(gè)死循環(huán)
        // 不停的等待,直到KEY的值為0,參會(huì)繼續(xù)走下面的邏輯,否則就會(huì)一直阻塞在這里
        while (getCount() > 0) {
            // waiting for open state
            RedissonCountDownLatchEntry entry = getEntry();
            if (entry != null) {
                entry.getLatch().await();
            }
        }
    } finally {
        unsubscribe(future);
    }
}

三、demo執(zhí)行結(jié)果圖

  1. 剛開始main線程設(shè)置必須有三個(gè)線程/客戶端執(zhí)行countDown,也就是獲取鎖成功
  2. 接下來(lái)三個(gè)線程/客戶端成功的獲取了鎖
  3. 最后三個(gè)線程獲取鎖執(zhí)行,執(zhí)行countDown邏輯后,主線程才會(huì)繼續(xù)執(zhí)行,否則就會(huì)一直阻塞住
CountDownLatch.png

四、總結(jié)

  1. 到此為止,Redisson的源碼基本上已經(jīng)分析差不多了,其實(shí)還有一些環(huán)境沒(méi)有發(fā)出來(lái),因?yàn)橐黄恼虏幌胩L(zhǎng),所以后面會(huì)陸陸續(xù)續(xù)的把redisson源碼的其他部分發(fā)出來(lái)
  2. 接下來(lái)就要分析zk的分布式鎖了,其實(shí)對(duì)比Redis分布式鎖和zk的分布式鎖的優(yōu)缺點(diǎn),全方面的進(jìn)行對(duì)比之后,在我們的實(shí)際業(yè)務(wù)開發(fā)中,我們才會(huì)知道,到底哪個(gè)分布式鎖更加適合我們,因?yàn)?,沒(méi)有最好,只有最合適。
  3. 大家一起努力,加油。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容