Redisson 分布式鎖機制

Redisson 是一個用於 Java 的分布式和分散式應用程序的高級框架,提供了多種分布式數據結構和服務。其中,分布式鎖是 Redisson 提供的重要功能之一。

分布式鎖的設計旨在在多個應用程序實例或多個線程之間實現協調,以確保只有一個實例(或線程)可以訪問關鍵區域,防止資源的併發競爭,確保數據的一致性和可靠性。

Redisson 分布式鎖的設計思想如下:

  1. 鎖的獲取與釋放: 使用 RLock 接口進行鎖的操作,可以通過 tryLock 方法嘗試獲取鎖,並通過 unlock 方法釋放鎖。

  2. 鎖的可重入性: Redisson 支持可重入鎖,即同一個線程可以多次獲取同一把鎖,且每次獲取都需要對應的解鎖操作。

  3. 過期時間: 可以為每個鎖設置過期時間,避免因為應用程序異常導致鎖一直佔用。

  4. 阻塞與非阻塞: tryLock 方法可以指定等待的時間,如果無法立即獲取到鎖,可以阻塞或者等待一段時間。

  5. 監視狀態: Redisson 提供了監視機制,可以監視鎖的狀態,如果鎖被釋放,通知等待的實例進行競爭。

  6. 分佈式: Redisson 鎖是基於 Redis 實現的,因此可以實現分佈式環境下的鎖。

使用 Redisson 分布式鎖時需要注意以下幾點:

  • 鎖的名稱應該是全局唯一的,以確保不同的實例可以協調。

  • 鎖的獲取和釋放應該處於同一個代碼區塊內,確保鎖在正確的時機被釋放。

  • 選擇適當的鎖的超時時間和等待策略,以避免死鎖和無限等待。

測試範例


import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.LongCodec;
import org.redisson.config.Config;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class RedissonLockTest {

    public static final String REDISSON_LOCK = "redisson";

    public static void main(String[] args) throws InterruptedException {
        RedissonClient redissonClient = redissonClient();
        try {
            RLock rLock = redissonClient.getLock(REDISSON_LOCK);

            // 嘗試鎖定
            rLock.tryLock();

            printLog("using lock.");

            // 創建一個新的線程
            List.of(3,5).forEach(o -> newAThread(redissonClient, o));
//            newAThread(redissonClient);

            int second = 5;
            printLog(String.format("sleep %s(s).", second));
            TimeUnit.SECONDS.sleep(second);

            // 如果當前線程鎖住並且持有該鎖
            if (rLock.isLocked() && rLock.isHeldByCurrentThread())
                rLock.unlock();

            printLog("release lock success.");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            TimeUnit.SECONDS.sleep(20);

            // 關閉 Redisson 客戶端
            redissonClient.shutdown();
        }

        TimeUnit.SECONDS.sleep(2);
        System.exit(0);
    }

    public static void newAThread(RedissonClient redissonClient, int i) {
        new Thread(() -> {
            try {
                // 創建一個 Redisson 分布式鎖
                RLock anotherLock = redissonClient.getLock(REDISSON_LOCK);

                // 創建一個 Redisson 訂閱主題
                RTopic topic = redissonClient.getTopic("redisson_lock__channel:{redisson}", LongCodec.INSTANCE);
//                RPatternTopic patternTopic = redissonClient.getPatternTopic("*", LongCodec.INSTANCE); // 亦可使用Regex 方式

                // 添加訂閱者到訂閱主題
                int topicId = topic.addListener(Long.class, (channel, msg) -> printLog(String.format("RTopic => channel:%s, message:%s", channel, msg)));

                // 嘗試鎖住
//                boolean lockSuccess = anotherLock.tryLock(2, TimeUnit.SECONDS);
                boolean lockSuccess = anotherLock.tryLock(5, 1, TimeUnit.SECONDS); // 等待時間內收到釋放鎖消息,則重新去競爭鎖
//                boolean lockSuccess = anotherLock.tryLock(2, 1, TimeUnit.SECONDS); // 等待時間內未獲得鎖直接回傳結果。
//                boolean lockSuccess = anotherLock.tryLock(); // 未設置等待時間,則直接判斷當前是否取到鎖
                if (lockSuccess) {
                    printLog("try lock success.");

                    TimeUnit.SECONDS.sleep(i);

                    // 如果當前線程鎖住並且持有該鎖
                    if (anotherLock.isLocked() && anotherLock.isHeldByCurrentThread()) {
                        anotherLock.unlock();
                        printLog("release lock success.");
                    }
                } else {
                    printLog("try lock failed.");
                }

                // 移除訂閱者
                topic.removeListener(topicId);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "Thread_" + i).start();
    }


    static void printLog(String message) {
        System.out.println(String.format("%s - [%s] -> %s", LocalDateTime.now(), Thread.currentThread().getName(), message));
    }

    private static RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://172.20.160.120:6379");
        config.setLockWatchdogTimeout(TimeUnit.SECONDS.toMillis(10));
        return Redisson.create(config);
    }
}

總而言之,Redisson 提供了方便易用的 API 來實現分布式鎖,這有助於處理多實例或多線程的併發問題,確保數據操作的一致性和可靠性。

Last updated