最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redisson分布式信號量RSemaphore的使用超詳細(xì)講解

 更新時間:2023年02月11日 11:19:18   作者:每天都要進(jìn)步一點點  
這篇文章主要介紹了Redisson分布式信號量RSemaphore的使用,基于Redis的Redisson的分布式信號量RSemaphore采用了與java.util.concurrent.Semaphore相似的接口和用法

本篇文章基于redisson-3.17.6版本源碼進(jìn)行分析

一、RSemaphore的使用

@Test
public void testRSemaphore() {
    Config config = new Config();
    config.useSingleServer().setAddress("redis://127.0.0.1:6379");
    RedissonClient redissonClient = Redisson.create(config);
    RSemaphore rSemaphore = redissonClient.getSemaphore("semaphore");
    // 設(shè)置5個許可,模擬五個停車位
    rSemaphore.trySetPermits(5);
    // 創(chuàng)建10個線程,模擬10輛車過來停車
    for (int i = 1; i <= 10; i++) {
        new Thread(() -> {
            try {
                rSemaphore.acquire();
                System.out.println(Thread.currentThread().getName() + "進(jìn)入停車場...");
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(100));
                System.out.println(Thread.currentThread().getName() + "離開停車場...");
                rSemaphore.release();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "A" + i).start();
    }
    try {
        TimeUnit.MINUTES.sleep(1);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

二、RSemaphore設(shè)置許可數(shù)量

初始化RSemaphore,需要調(diào)用trySetPermits()設(shè)置許可數(shù)量:

/**
 * 嘗試設(shè)置許可數(shù)量,設(shè)置成功,返回true,否則返回false
 */
boolean trySetPermits(int permits);

trySetPermits()內(nèi)部調(diào)用了trySetPermitsAsync():

// 異步設(shè)置許可
@Override
public RFuture<Boolean> trySetPermitsAsync(int permits) {
    RFuture<Boolean> future = commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            // 判斷分布式信號量的key是否存在,如果不存在,才設(shè)置
            "local value = redis.call('get', KEYS[1]); " +
                    "if (value == false) then "
                    // set "semaphore" permits
                    // 使用String數(shù)據(jù)結(jié)構(gòu)設(shè)置信號量的許可數(shù)
                    + "redis.call('set', KEYS[1], ARGV[1]); "
                    // 發(fā)布一條消息到redisson_sc:{semaphore}通道
                    + "redis.call('publish', KEYS[2], ARGV[1]); "
                    // 設(shè)置成功,返回1
                    + "return 1;"
                    + "end;"
                    // 否則返回0
                    + "return 0;",
            Arrays.asList(getRawName(), getChannelName()), permits);
    if (log.isDebugEnabled()) {
        future.thenAccept(r -> {
            if (r) {
                log.debug("permits set, permits: {}, name: {}", permits, getName());
            } else {
                log.debug("unable to set permits, permits: {}, name: {}", permits, getName());
            }
        });
    }
    return future;
}

可以看到,設(shè)置許可數(shù)量底層使用LUA腳本,實際上就是使用redis的String數(shù)據(jù)結(jié)構(gòu),保存了我們指定的許可數(shù)量。如下圖:

參數(shù)說明:

  • KEYS[1]: 我們指定的分布式信號量key,例如redissonClient.getSemaphore("semaphore")中的"semaphore")
  • KEYS[2]: 釋放鎖的channel名稱,redisson_sc:{分布式信號量key},在本例中,就是redisson_sc:{semaphore}
  • ARGV[1]: 設(shè)置的許可數(shù)量

總結(jié)設(shè)置許可執(zhí)行流程為:

  • get semaphore,獲取到semaphore信號量的當(dāng)前的值
  • 第一次數(shù)據(jù)為0, 然后使用set semaphore 3,將這個信號量同時能夠允許獲取鎖的客戶端的數(shù)量設(shè)置為3。(注意到,如果之前設(shè)置過了信號量,將無法再次設(shè)置,直接返回0。想要更改信號量總數(shù)可以使用addPermits方法)
  • 然后redis發(fā)布一些消息,返回1

三、RSemaphore的加鎖流程

許可數(shù)量設(shè)置好之后,我們就可以調(diào)用acquire()方法獲取了,如果未傳入許可數(shù)量,默認(rèn)獲取一個許可。

public void acquire() throws InterruptedException {
    acquire(1);
}
public void acquire(int permits) throws InterruptedException {
    // 嘗試獲取鎖成功,直接返回
    if (tryAcquire(permits)) {
        return;
    }
    // 對于沒有獲取鎖的那些線程,訂閱redisson_sc:{分布式信號量key}通道的消息
    CompletableFuture<RedissonLockEntry> future = subscribe();
    semaphorePubSub.timeout(future);
    RedissonLockEntry entry = commandExecutor.getInterrupted(future);
    try {
        // 不斷循環(huán)嘗試獲取許可
        while (true) {
            if (tryAcquire(permits)) {
                return;
            }
            entry.getLatch().acquire();
        }
    } finally {
        // 取消訂閱
        unsubscribe(entry);
    }
//        get(acquireAsync(permits));
}

可以看到,獲取許可的核心邏輯在tryAcquire()方法中,如果tryAcquire()返回true說明獲取許可成功,直接返回;如果返回false,說明當(dāng)前沒有許可可以使用,則對于沒有獲取鎖的那些線程,訂閱redisson_sc:{分布式信號量key}通道的消息,并通過死循環(huán)不斷嘗試獲取鎖。

我們看一下tryAcquire()方法的邏輯,內(nèi)部調(diào)用了tryAcquireAsync()方法:

// 異步獲取許可
@Override
public RFuture<Boolean> tryAcquireAsync(int permits) {
    if (permits < 0) {
        throw new IllegalArgumentException("Permits amount can't be negative");
    }
    if (permits == 0) {
        return new CompletableFutureWrapper<>(true);
    }
    return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
              // 獲取當(dāng)前剩余的許可數(shù)量
              "local value = redis.call('get', KEYS[1]); " +
              // 許可不為空,并且許可數(shù)量 大于等于 當(dāng)前線程申請的許可數(shù)量        
              "if (value ~= false and tonumber(value) >= tonumber(ARGV[1])) then " +
                  // 通過decrby減少剩余可用許可    
                  "local val = redis.call('decrby', KEYS[1], ARGV[1]); " +
                  // 返回1    
                  "return 1; " +
              "end; " +
              // 其它情況,返回0        
              "return 0;",
              Collections.<Object>singletonList(getRawName()), permits);
}

從源碼可以看到,獲取許可就是操作redis中的數(shù)據(jù),首先獲取到redis中剩余的許可數(shù)量,只有當(dāng)剩余的許可數(shù)量大于線程申請的許可數(shù)量時,才獲取成功,返回1;否則獲取失敗,返回0;

總結(jié)加鎖執(zhí)行流程為:

  • get semaphore,獲取到一個當(dāng)前的值,比如說是3,3 > 1
  • decrby semaphore 1,將信號量允許獲取鎖的客戶端的數(shù)量遞減1,變成2
  • decrby semaphore 1
  • decrby semaphore 1
  • 執(zhí)行3次加鎖后,semaphore值為0
  • 此時如果再來進(jìn)行加鎖則直接返回0,然后進(jìn)入死循環(huán)去獲取鎖

四、RSemaphore的解鎖流程

通過前面對RSemaphore獲取鎖的分析,我們很容易能猜到,釋放鎖,無非就是歸還許可數(shù)量到redis中。我們查看具體的源碼:

public RFuture<Void> releaseAsync(int permits) {
    if (permits < 0) {
        throw new IllegalArgumentException("Permits amount can't be negative");
    }
    if (permits == 0) {
        return new CompletableFutureWrapper<>((Void) null);
    }
    RFuture<Void> future = commandExecutor.evalWriteAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.EVAL_VOID,
            // 通過incrby增加許可數(shù)量
            "local value = redis.call('incrby', KEYS[1], ARGV[1]); " +
                    // 發(fā)布一條消息到redisson_sc:{semaphore}中
                    "redis.call('publish', KEYS[2], value); ",
            Arrays.asList(getRawName(), getChannelName()), permits);
    if (log.isDebugEnabled()) {
        future.thenAccept(o -> {
            log.debug("released, permits: {}, name: {}", permits, getName());
        });
    }
    return future;
}

到此這篇關(guān)于Redisson分布式信號量RSemaphore的使用超詳細(xì)講解的文章就介紹到這了,更多相關(guān)Redisson RSemaphore內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

  • MyBatis執(zhí)行動態(tài)SQL的方法

    MyBatis執(zhí)行動態(tài)SQL的方法

    今天小編就為大家分享一篇關(guān)于MyBatis執(zhí)行動態(tài)SQL的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Java循環(huán)隊列原理與用法詳解

    Java循環(huán)隊列原理與用法詳解

    這篇文章主要介紹了Java循環(huán)隊列原理與用法,結(jié)合實例形式詳細(xì)分析了Java循環(huán)隊列基本概念、原理、用法及操作注意事項,需要的朋友可以參考下
    2020-03-03
  • cmd中javac和java使用及注意事項詳解

    cmd中javac和java使用及注意事項詳解

    這篇文章主要介紹了cmd中javac和java使用及注意事項詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • springBoot整合shiro如何解決讀取不到@value值問題

    springBoot整合shiro如何解決讀取不到@value值問題

    這篇文章主要介紹了springBoot整合shiro如何解決讀取不到@value值問題,具有很好的參考價值,希望對大家有所幫助,
    2023-08-08
  • SpringCloud OpenFeign概述與使用

    SpringCloud OpenFeign概述與使用

    OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成
    2023-01-01
  • 淺談Spring Bean的基本配置

    淺談Spring Bean的基本配置

    這篇文章主要介紹了淺談Spring Bean的基本配置,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java Spring的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-05-05
  • SpringBoot監(jiān)視器的具體使用

    SpringBoot監(jiān)視器的具體使用

    Spring Boot監(jiān)視器是一個用于監(jiān)控和管理Spring Boot應(yīng)用程序的工具集,本文就來介紹一下SpringBoot監(jiān)視器的具體使用,感興趣的可以了解一下
    2025-01-01
  • Java Classloader機(jī)制用法代碼解析

    Java Classloader機(jī)制用法代碼解析

    這篇文章主要介紹了Java Classloader機(jī)制用法代碼解析,涉及JDK默認(rèn)ClassLoader,雙親委托模型,自定義ClassLoader等相關(guān)內(nèi)容,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • Java Stream流之求和的實現(xiàn)

    Java Stream流之求和的實現(xiàn)

    這篇文章主要介紹了Java Stream流之求和的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 最新評論

    辛集市| 夏邑县| 丁青县| 年辖:市辖区| 扎兰屯市| 合山市| 隆德县| 甘谷县| 内丘县| 车致| 桐梓县| 贡嘎县| 五大连池市| 莱州市| 蒙阴县| 尉氏县| 南木林县| 囊谦县| 兴安县| 图木舒克市| 正蓝旗| 定日县| 大石桥市| 阿拉善左旗| 正宁县| 兴国县| 神池县| 静宁县| 蓬安县| 云南省| 阜新| 广昌县| 津南区| 盈江县| 通山县| 汽车| 壶关县| 河津市| 肇源县| 油尖旺区| 静宁县|