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

SpringBoot 整合Lock4j 分布式鎖深度使用案例講解

 更新時(shí)間:2025年07月28日 10:05:21   作者:逆風(fēng)飛翔的小叔  
本文介紹SpringBoot分布式鎖框架Lock4j,支持Redis、Zookeeper等多種存儲(chǔ)實(shí)現(xiàn),通過注解和AOP簡化鎖管理,提供并發(fā)控制、防止重復(fù)提交等場景應(yīng)用,并支持自定義執(zhí)行器和key生成策略,適用于高并發(fā)和異構(gòu)系統(tǒng)需求,感興趣的朋友一起看看吧

一、前言

日常開發(fā)中使用springboot框架進(jìn)行微服務(wù)開發(fā)時(shí),遇到需要控制并發(fā)造成的問題時(shí),比較常用的做法是使用分布式鎖進(jìn)行控制,基于分布式鎖的實(shí)現(xiàn),到目前為止也有很多開源實(shí)現(xiàn),使用比較多的像基于redis的分布式鎖,基于zookeeper的分布式鎖,本文再來介紹另一種比較高效的分布式鎖實(shí)現(xiàn),即lock4j,將通過案例演示下如何使用lock4j做分布式鎖。

二、Lock4j 概述

2.1 Lock4j 介紹

2.1.1 Lock4j 是什么

Lock4j 是一個(gè)基于 Spring Boot 的分布式鎖框架,旨在簡化分布式系統(tǒng)中鎖的實(shí)現(xiàn)和管理。項(xiàng)目入口:lock4j: 基于Spring AOP 的聲明式和編程式分布式鎖,支持RedisTemplate、Redisson、Zookeeper

2.1.2 Lock4j 主要特征

Lock4j是一個(gè)輕量級(jí)的分布式鎖框架,它支持多種鎖實(shí)現(xiàn),包括Redis、Zookeeper等,并通過Spring AOP進(jìn)行集成,使得開發(fā)者可以輕松地在Spring應(yīng)用中使用分布式鎖。它的設(shè)計(jì)目標(biāo)是簡單易用,同時(shí)提供高性能和高可靠性。具體來說,包括下面的主要特征:

  • 注解驅(qū)動(dòng):通過簡單的注解即可實(shí)現(xiàn)方法級(jí)別的鎖控制
  • 多種鎖實(shí)現(xiàn):支持多種底層鎖實(shí)現(xiàn)方式
  • 可擴(kuò)展:支持自定義鎖的實(shí)現(xiàn)
  • 與Spring生態(tài)集成:無縫集成Spring框架

2.1.3 Lock4j 技術(shù)特點(diǎn)

Lock4j 作為一款上手簡單,使用高效的分布式緩存技術(shù)框架,具備如下特點(diǎn)和優(yōu)勢:

  • 使用高效:
    • Lock4j采用了高效的鎖機(jī)制,能夠在高并發(fā)場景下保持穩(wěn)定的性能。其底層實(shí)現(xiàn)充分利用了Redis和Zookeeper等存儲(chǔ)系統(tǒng)的特性,確保鎖操作的快速響應(yīng)。
  • 簡單易用:
    • 通過簡單的API和Spring AOP集成,開發(fā)者可以快速上手并實(shí)現(xiàn)分布式鎖。Lock4j提供了詳細(xì)的文檔和示例代碼,幫助開發(fā)者快速理解和使用。
  • 鎖支持類型豐富:
    • 支持多種底層存儲(chǔ),滿足不同應(yīng)用場景的需求。無論是高性能的Redis,還是強(qiáng)一致性的Zookeeper,Lock4j都能提供相應(yīng)的支持。
  • 提供監(jiān)控和日志:
    • 提供豐富的監(jiān)控和日志功能,幫助開發(fā)者了解鎖的使用情況和性能表現(xiàn)。通過監(jiān)控界面,開發(fā)者可以實(shí)時(shí)查看鎖的狀態(tài)和性能指標(biāo)。

2.2 Lock4j 支持的鎖類型

Lock4j支持多種類型鎖,通過底層存儲(chǔ)(如Redis或Zookeeper)來實(shí)現(xiàn)分布式鎖。支持的鎖類型包括:

  • Redis鎖:基于Redis實(shí)現(xiàn)的分布式鎖
  • Zookeeper鎖:基于Zookeeper實(shí)現(xiàn)的分布式鎖
  • 數(shù)據(jù)庫鎖:基于數(shù)據(jù)庫實(shí)現(xiàn)的鎖
  • 內(nèi)存鎖:本地JVM鎖(適用于單機(jī)環(huán)境)

2.3 Lock4j 工作原理

什么是鎖?鎖是一種同步機(jī)制,用于控制多線程對(duì)共享資源的訪問。在分布式系統(tǒng)中,鎖的實(shí)現(xiàn)更加復(fù)雜,因?yàn)樾枰诙鄠€(gè)節(jié)點(diǎn)之間進(jìn)行協(xié)調(diào)。Lock4j 作為分布式鎖框架,其核心原理是通過協(xié)調(diào)多個(gè)分布式節(jié)點(diǎn)對(duì)共享資源的訪問,確保在分布式環(huán)境下同一時(shí)間只有一個(gè)節(jié)點(diǎn)能夠執(zhí)行受保護(hù)的代碼塊。Lock4j通過底層存儲(chǔ)(如Redis、Zookeeper等)來實(shí)現(xiàn)分布式鎖。不同的存儲(chǔ)組件在具體的實(shí)現(xiàn)上稍有差別,但是其核心原理是類似的,下面是Lock4j 的基本工作原理:

  • 獲取鎖:
    • 當(dāng)一個(gè)節(jié)點(diǎn)需要訪問共享資源時(shí),它會(huì)向底層存儲(chǔ)發(fā)送請(qǐng)求以獲取鎖。請(qǐng)求中包含鎖的唯一標(biāo)識(shí)和節(jié)點(diǎn)信息。
  • 鎖的持有:
    • 如果鎖可用,節(jié)點(diǎn)將持有該鎖,并可以安全地訪問共享資源。持有鎖的節(jié)點(diǎn)需要定期向存儲(chǔ)系統(tǒng)發(fā)送心跳信號(hào),以保持鎖的有效性。
  • 釋放鎖:
    • 訪問完成后,節(jié)點(diǎn)會(huì)釋放鎖,使得其他節(jié)點(diǎn)可以獲取鎖并訪問資源。釋放鎖時(shí),節(jié)點(diǎn)需要確保鎖的狀態(tài)已更新,以避免其他節(jié)點(diǎn)誤認(rèn)為鎖仍然被持有。

2.4 Lock4j 應(yīng)用場景

Lock4j 作為分布式鎖框架,適用于各種需要協(xié)調(diào)分布式系統(tǒng)資源訪問的場景。下面列舉了幾種常用的應(yīng)用場景:

  • 防止重復(fù)提交/重復(fù)請(qǐng)求處理
    • 典型場景
    • 用戶快速多次點(diǎn)擊提交按鈕
    • 消息隊(duì)列消費(fèi)者重復(fù)消費(fèi)同一條消息
    • 定時(shí)任務(wù)重復(fù)執(zhí)行
  • 分布式鎖并發(fā)控制
    • 典型場景
    • 全局配置更新
    • 賬戶余額變更
    • 庫存扣減(防止超賣)
  • 定時(shí)任務(wù)防重復(fù)執(zhí)行
    • 典型場景
      • 分布式環(huán)境下多個(gè)實(shí)例的定時(shí)任務(wù)
      • 長時(shí)間執(zhí)行的批處理任務(wù)
  • 分布式緩存
    • 典型場景
      • 緩存擊穿保護(hù)
      • 緩存一致性維護(hù)
  • 關(guān)鍵業(yè)務(wù)流程串行化處理
    • 典型場景
      • 支付訂單處理
      • 文件導(dǎo)入導(dǎo)出
      • 數(shù)據(jù)遷移任務(wù)
  • 分布式文件系統(tǒng)
    • 在分布式文件系統(tǒng)中,多個(gè)節(jié)點(diǎn)可能會(huì)同時(shí)訪問和修改文件。
    • 通過使用Lock4j,可確保文件的一致性,避免數(shù)據(jù)損壞。例如,在云存儲(chǔ)系統(tǒng)中,多個(gè)節(jié)點(diǎn)可能會(huì)同時(shí)上傳和下載文件,通過分布式鎖可以確保文件數(shù)據(jù)的完整性。

三、springboot 整合lock4j使用案例

接下來通過案例詳細(xì)演示如何基于springboot整合lock4j進(jìn)行代碼操作詳解。

3.1 前置準(zhǔn)備

3.1. 1 導(dǎo)入依賴

創(chuàng)建一個(gè)springboot工程,導(dǎo)入下面的依賴

  • <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>
        <!-- 使用lock4j實(shí)現(xiàn)分布式鎖   https://gitee.com/baomidou/lock4j/-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-core</artifactId>
            <version>2.2.5</version>
        </dependency>
        <!--添加開源分布式鎖Lock4j-->
        <!--若使用redisTemplate作為分布式鎖底層,則需要引入-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
            <version>2.2.5</version>
        </dependency>
    </dependencies>

3.2 基于Redis實(shí)現(xiàn)分布式鎖

基于Redis的實(shí)現(xiàn)是很常用的整合使用方式,當(dāng)使用 Redis 作為底層存儲(chǔ)時(shí),Lock4j 主要依賴 Redis 的以下特性:

  • SETNX 命令(或 SET 命令的 NX 選項(xiàng)):原子性地設(shè)置鍵值,只有鍵不存在時(shí)才會(huì)設(shè)置成功
  • 過期時(shí)間:避免死鎖
  • Lua 腳本:保證解鎖操作的原子性

下面看具體的整合和使用過程。

3.2.1 添加配置文件信息

在工程的配置文件中添加下面的信息

  • 需要注意的是,配置文件中設(shè)置的lock4j的幾個(gè)核心參數(shù)信息是全局生效的,比如acquire-timeout,這里全局設(shè)置的是3秒,但如果你在注解中再次配置了,則會(huì)覆蓋配置文件中的這個(gè)值;
server:
  port: 8082
lock4j:
  type: redis
  acquire-timeout: 3000  # 獲取鎖超時(shí)時(shí)間(毫秒)
  expire: 30000   # 鎖過期時(shí)間(毫秒)
  retry-interval: 100  # 獲取鎖失敗重試間隔(毫秒)
  #redis的鏈接配置信息
  redis:
    host: localhost
    database: 1
    port: 6379

3.2.2 添加測試接口

在工程中增加如下接口,方便測試

package com.congge.web;
import com.baomidou.lock.annotation.Lock4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LockController {
    //localhost:8082/testLock
    @Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000)
    @GetMapping("/testLock")
    public Object testLock(){
        long threadId = Thread.currentThread().getId();
        System.out.println(threadId + " : 獲取到了鎖,準(zhǔn)備執(zhí)行業(yè)務(wù)邏輯");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "testLock";
    }
}

在上面這段代碼中,在接口上面增加了一個(gè) @Lock4j的注解,使用該注解即可實(shí)現(xiàn)分布式鎖的功能,并且設(shè)置了注解中其他的參數(shù),其核心參數(shù)包括:

  • key:鎖的鍵,支持SpEL表達(dá)式
  • expire:鎖的過期時(shí)間(毫秒)
  • timeout:獲取鎖的超時(shí)時(shí)間(毫秒)
  • retry:獲取鎖失敗后的重試間隔(毫秒)

3.2.3 效果測試

在postman中模擬一下多線程的并發(fā)測試

然后通過控制臺(tái)的結(jié)果輸出不難看出,多個(gè)線程請(qǐng)求過來的時(shí)候,由于分布式鎖的存在,所以未獲取到鎖的請(qǐng)求將會(huì)進(jìn)行排隊(duì),等待前面的線程釋放鎖,拿到鎖之后才能執(zhí)行

3.3 基于Redission 實(shí)現(xiàn)

使用Redission 的實(shí)現(xiàn)方式與Redis的實(shí)現(xiàn)差不多,首先需要導(dǎo)入下面的依賴,配置文件信息和接口代碼不用動(dòng),然后啟動(dòng)工程后再次測試,可以得到相同的效果。

3.4 基于Zookeeper實(shí)現(xiàn)

基于 Zookeeper 的實(shí)現(xiàn)主要是利用了zk的臨時(shí)順序節(jié)點(diǎn)來實(shí)現(xiàn),還記得在使用zk實(shí)現(xiàn)分布式鎖的機(jī)制嗎,主要流程如下:

  • 在指定路徑下創(chuàng)建臨時(shí)順序節(jié)點(diǎn)
  • 判斷當(dāng)前節(jié)點(diǎn)是否為最小序號(hào)節(jié)點(diǎn)
  • 如果是則獲取鎖,否則監(jiān)聽前一個(gè)節(jié)點(diǎn)的刪除事件

下面來看如何在代碼中集成和使用。

3.4.1 啟動(dòng)zk服務(wù)

為了在代碼中集成并使用zookeeper,需要本地或服務(wù)器啟動(dòng)一個(gè)zookeeper服務(wù),這里直接在本機(jī)啟動(dòng)

3.4.2 導(dǎo)入下面的依賴

在pom文件中導(dǎo)入下面的依賴

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>lock4j-zookeeper-spring-boot-starter</artifactId>
    <version>2.2.5</version>
</dependency>

3.4.3 添加配置信息

在配置文件中添加下面的配置信息

#使用redis作為分布式鎖
spring:
  coordinate:
    zookeeper:
      zkServers: 127.0.0.1:2181

3.4.4 添加測試接口

增加一個(gè)測試接口方便測試看效果

//localhost:8082/testZkLock
@Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000, executor = ZookeeperLockExecutor.class)
@GetMapping("/testZkLock")
public Object testZkLock(){
    long threadId = Thread.currentThread().getId();
    System.out.println(threadId + " : 獲取到了zk的鎖,準(zhǔn)備執(zhí)行業(yè)務(wù)邏輯");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return "testLock";
}

3.4.5 模擬并發(fā)測試

在postman中,使用并發(fā)調(diào)用接口的方式進(jìn)行測試

通過控制臺(tái)輸出效果可以看到,有了分布式鎖的控制,可以確保請(qǐng)求的有序執(zhí)行

四、lock4j功能擴(kuò)展

如果默認(rèn)的配置還不能滿足實(shí)際的需求場景時(shí),還可以使用lock4j提供的一些擴(kuò)展點(diǎn)做補(bǔ)充,下面介紹幾個(gè)點(diǎn)。

4.1 自定義執(zhí)行器作用

lock4j 的自定義執(zhí)行器主要用于擴(kuò)展和定制分布式鎖的實(shí)現(xiàn)方式

4.1.1 支持不同的分布式鎖實(shí)現(xiàn)

作用:允許集成各種分布式鎖技術(shù),而不僅限于框架默認(rèn)提供的幾種。

典型場景

  • 當(dāng)項(xiàng)目使用非主流分布式協(xié)調(diào)服務(wù)時(shí)(如非Redis、Zookeeper等)
  • 需要接入公司自研的分布式鎖服務(wù)
  • 使用云服務(wù)商特有的分布式鎖服務(wù)(如AWS DynamoDB Lock Client)

4.1.2 定制鎖的獲取和釋放邏輯

作用:完全控制鎖的獲取和釋放過程,實(shí)現(xiàn)特殊業(yè)務(wù)需求。

典型場景

  • 需要實(shí)現(xiàn)特定等待策略(如指數(shù)退避)
  • 需要添加額外的鎖校驗(yàn)邏輯(如業(yè)務(wù)狀態(tài)檢查)
  • 需要記錄詳細(xì)的鎖競爭指標(biāo)和日志

4.1.3 適配特殊業(yè)務(wù)需求

作用:解決標(biāo)準(zhǔn)分布式鎖無法滿足的特殊業(yè)務(wù)場景。

典型場景

  • 需要實(shí)現(xiàn)租約機(jī)制(lease-based locking)
  • 需要支持不同級(jí)別的鎖(如讀鎖/寫鎖)
  • 需要實(shí)現(xiàn)鎖的自動(dòng)續(xù)期功能

4.1.4 性能優(yōu)化

作用:針對(duì)特定環(huán)境優(yōu)化鎖的性能表現(xiàn)。

典型場景

  • 針對(duì)高并發(fā)場景優(yōu)化鎖實(shí)現(xiàn)
  • 減少網(wǎng)絡(luò)往返次數(shù)
  • 實(shí)現(xiàn)本地緩存加速

4.1.5 增強(qiáng)可靠性

作用:提供更健壯的鎖機(jī)制,防止極端情況下的問題。

典型場景

  • 處理時(shí)鐘漂移問題
  • 實(shí)現(xiàn)更安全的鎖釋放機(jī)制
  • 防止鎖過期但業(yè)務(wù)未完成的情況

4.1.6 統(tǒng)一鎖管理

作用:在異構(gòu)環(huán)境中提供一致的鎖管理接口。

典型場景

  • 混合使用多種鎖實(shí)現(xiàn)(如部分用Redis,部分用數(shù)據(jù)庫)
  • 需要統(tǒng)一的監(jiān)控和管理界面
  • 實(shí)現(xiàn)鎖的降級(jí)策略

4.2 自定義執(zhí)行器使用

接下來通過兩個(gè)案例演示下如何使用自定義執(zhí)行器

4.2.1 基于zk的自定義執(zhí)行器

添加一個(gè)自定義類,繼承抽象類AbstractLockExecutor并重寫內(nèi)部的幾個(gè)核心方法,參考下面的代碼

package com.congge.config;
import com.baomidou.lock.executor.AbstractLockExecutor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Slf4j
@RequiredArgsConstructor
/**
 * 基于zk的分布式鎖執(zhí)行器 , 使用CuratorFramework作為ZooKeeper客戶端,實(shí)現(xiàn)鎖的獲取和釋放。
 */
@Component
public class MyZookeeperLockExecutor extends AbstractLockExecutor<InterProcessMutex> {
    private final CuratorFramework curatorFramework;
    /**
     * 嘗試獲取分布式鎖
     *
     * @param lockKey 鎖的關(guān)鍵字,用于在ZooKeeper中創(chuàng)建鎖節(jié)點(diǎn)的路徑。
     * @param lockValue 鎖的值,可以用于進(jìn)一步標(biāo)識(shí)鎖。
     * @param expire 鎖的過期時(shí)間,未使用。
     * @param acquireTimeout 獲取鎖的超時(shí)時(shí)間。
     * @return 如果成功獲取鎖,返回InterProcessMutex實(shí)例;否則返回null。
     */
    @Override
    public InterProcessMutex acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
        System.out.println("進(jìn)入了zk的自定義鎖執(zhí)行器");
        // 檢查CuratorFramework實(shí)例是否已啟動(dòng)
        if (!CuratorFrameworkState.STARTED.equals(curatorFramework.getState())) {
            log.warn("instance must be started before calling this method");
            return null;
        }
        // 構(gòu)建鎖節(jié)點(diǎn)的路徑
        String nodePath = "/curator/lock4j/%s";
        try {
            // 創(chuàng)建InterProcessMutex實(shí)例,并嘗試獲取鎖
            InterProcessMutex mutex = new InterProcessMutex(curatorFramework, String.format(nodePath, lockKey));
            final boolean locked = mutex.acquire(acquireTimeout, TimeUnit.MILLISECONDS);
            // 根據(jù)獲取鎖的結(jié)果,返回相應(yīng)的鎖實(shí)例或null
            return obtainLockInstance(locked, mutex);
        } catch (Exception e) {
            // 獲取鎖過程中發(fā)生異常,返回null
            return null;
        }
    }
    /**
     * 釋放分布式鎖。
     *
     * @param key 鎖的關(guān)鍵字,與獲取鎖時(shí)使用的key相同。
     * @param value 鎖的值,與獲取鎖時(shí)使用的value相同。
     * @param lockInstance 鎖實(shí)例,用于釋放鎖。
     * @return 如果成功釋放鎖,返回true;否則返回false。
     */
    @Override
    public boolean releaseLock(String key, String value, InterProcessMutex lockInstance) {
        System.out.println("進(jìn)入了zk的自定義鎖執(zhí)行器釋放鎖");
        try {
            // 直接釋放鎖
            lockInstance.release();
        } catch (Exception e) {
            // 釋放鎖過程中發(fā)生異常,記錄日志并返回false
            log.warn("zookeeper lock release error", e);
            return false;
        }
        // 成功釋放鎖,返回true
        return true;
    }
}

添加如下測試接口,在自定義注解中指定executor 為自定義的類即可

//localhost:8082/testZkLock
//@Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000, executor = ZookeeperLockExecutor.class)
@Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000, executor  = MyZookeeperLockExecutor.class)
@GetMapping("/testZkLock")
public Object testZkLock(){
    long threadId = Thread.currentThread().getId();
    System.out.println(threadId + " : 獲取到了zk的鎖,準(zhǔn)備執(zhí)行業(yè)務(wù)邏輯");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return "testLock";
}

啟動(dòng)工程后,使用接口工具模擬并發(fā)測試

通過控制臺(tái)輸出日志可以看到獲取鎖的邏輯走到了自定義的zk執(zhí)行器里面了

4.2.2 基于Redis的自定義執(zhí)行器

同樣,如果要使用基于Redis的自定義執(zhí)行器,也需要首先自定義一個(gè)類繼承AbstractLockExecutor抽象類,參考下面的代碼,結(jié)合代碼理解

package com.congge.config;
import com.baomidou.lock.executor.AbstractLockExecutor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
/**
 * Redis模板鎖執(zhí)行器,實(shí)現(xiàn)基于Redis的分布式鎖,使用StringRedisTemplate和Lua腳本實(shí)現(xiàn)鎖的獲取和釋放,提高鎖操作的原子性
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class MyRedisTemplateLockExecutor extends AbstractLockExecutor<String> {
    /**
     * 獲取鎖的Lua腳本。
     * 該腳本用于嘗試以NX選項(xiàng)設(shè)置鍵值對(duì),實(shí)現(xiàn)鎖的獲取。
     */
    private static final RedisScript<String> SCRIPT_LOCK = new DefaultRedisScript<>("return redis.call('set',KEYS[1]," +
            "ARGV[1],'NX','PX',ARGV[2])", String.class);
    /**
     * 釋放鎖的Lua腳本
     * 首先驗(yàn)證鎖是否由當(dāng)前持有者釋放,如果是,則刪除鎖
     */
    private static final RedisScript<String> SCRIPT_UNLOCK = new DefaultRedisScript<>(
            "if redis.call('get',KEYS[1]) " +
            "== ARGV[1] then return tostring(redis.call('del', KEYS[1])==1) else return 'false' end",
            String.class);
    /**
     * 表示鎖獲取成功的固定字符串。
     */
    private static final String LOCK_SUCCESS = "OK";
    /**
     * Redis字符串模板,用于執(zhí)行Redis操作。
     */
    private final StringRedisTemplate redisTemplate;
    /**
     * 嘗試獲取鎖
     * 使用Lua腳本在Redis中執(zhí)行SET命令,嘗試獲取鎖
     *
     * @param lockKey        鍵
     * @param lockValue      值,唯一的標(biāo)識(shí)
     * @param expire         鎖的期時(shí)間,單位為毫秒
     * @param acquireTimeout 獲取鎖的超時(shí)時(shí)間,單位為毫秒
     * @return 一個(gè)表示鎖實(shí)例的字符串,如果獲取鎖失敗,則為null
     */
    @Override
    public String acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
        System.out.println("嘗試獲取redis的鎖");
        String lock = redisTemplate.execute(SCRIPT_LOCK,
                redisTemplate.getStringSerializer(),
                redisTemplate.getStringSerializer(),
                Collections.singletonList(lockKey),
                lockValue, String.valueOf(expire));
        final boolean locked = LOCK_SUCCESS.equals(lock);
        return obtainLockInstance(locked, lock);
    }
    /**
     * 釋放鎖
     * 使用Lua腳本在Redis中執(zhí)行驗(yàn)證和刪除操作,以確保只有鎖的持有者才能釋放鎖
     *
     * @param key          鍵
     * @param value        值,用于驗(yàn)證鎖的所有權(quán)。
     * @param lockInstance 表示鎖實(shí)例的字符串
     * @return 表示釋放鎖是否成功的布爾值
     */
    @Override
    public boolean releaseLock(String key, String value, String lockInstance) {
        System.out.println("釋放鎖");
        String releaseResult = redisTemplate.execute(SCRIPT_UNLOCK,
                redisTemplate.getStringSerializer(),
                redisTemplate.getStringSerializer(),
                Collections.singletonList(key), value);
        return Boolean.parseBoolean(releaseResult);
    }
}

再添加一個(gè)測試接口,如下:

//localhost:8082/testRedisLock
@Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000, executor  = MyRedisTemplateLockExecutor.class)
@GetMapping("/testRedisLock")
public Object testRedisLock(){
    long threadId = Thread.currentThread().getId();
    System.out.println(threadId + " : 獲取到了redis的鎖,準(zhǔn)備執(zhí)行業(yè)務(wù)邏輯");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return "testLock";
}

使用接口工具模擬一下并發(fā)請(qǐng)求,如下效果

通過控制臺(tái)的輸出可以看到自定義的執(zhí)行器生效了

4.3 自定義鎖的key生成策略

默認(rèn)情況下,如果不做任何的設(shè)置,以redis為例,加鎖時(shí)生成的key的前綴統(tǒng)一為:lock4j,這個(gè)在配置文件中也可以做如下顯示的設(shè)置

加鎖過程中,可以看到如下效果,key的格式為 lock4j開頭

某些情況下,如果你想定制自己的key的生成策略,可以通過自定義一個(gè)類并實(shí)現(xiàn)LockKeyBuilder這個(gè)接口,如下:

package com.congge.config;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.baomidou.lock.LockKeyBuilder;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class DefaultLockKeyBuilder implements LockKeyBuilder {
    private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
    private static final ExpressionParser PARSER = new SpelExpressionParser();
    private BeanResolver beanResolver;
    public DefaultLockKeyBuilder(BeanFactory beanFactory) {
        this.beanResolver = new BeanFactoryResolver(beanFactory);
    }
    public String buildKey(MethodInvocation invocation, String[] definitionKeys) {
        System.out.println("開始構(gòu)建自定義的key");
        Method method = invocation.getMethod();
        return definitionKeys.length <= 1 && "".equals(definitionKeys[0]) ? "" : this.getSpelDefinitionKey(definitionKeys, method, invocation.getArguments());
    }
    protected String getSpelDefinitionKey(String[] definitionKeys, Method method, Object[] parameterValues) {
        StandardEvaluationContext context = new MethodBasedEvaluationContext((Object)null, method, parameterValues, NAME_DISCOVERER);
        context.setBeanResolver(this.beanResolver);
        List<String> definitionKeyList = new ArrayList(definitionKeys.length);
        String[] var6 = definitionKeys;
        int var7 = definitionKeys.length;
        for(int var8 = 0; var8 < var7; ++var8) {
            String definitionKey = var6[var8];
            if (definitionKey != null && !definitionKey.isEmpty()) {
                String key = (String)PARSER.parseExpression(definitionKey).getValue(context, String.class);
                definitionKeyList.add(key);
            }
        }
        return StringUtils.collectionToDelimitedString(definitionKeyList, ".", "", "");
    }
}

在buildkey的方法里面,你就可以根據(jù)自己的需求自定義key的生成策略了,再次請(qǐng)求接口,可以看到代碼已經(jīng)走到這里了

五、寫在最后

本文詳細(xì)介紹了分布式鎖Lock4j的使用,并通過案例代碼演示了其詳細(xì)的使用,希望對(duì)看到的同學(xué)有用哦,本篇到此結(jié)束,感謝觀看。

到此這篇關(guān)于SpringBoot 整合Lock4j 分布式鎖深度使用案例講解的文章就介紹到這了,更多相關(guān)SpringBoot Lock4j 分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • idea構(gòu)建web項(xiàng)目的超級(jí)詳細(xì)教程

    idea構(gòu)建web項(xiàng)目的超級(jí)詳細(xì)教程

    好多朋友在使用IDEA創(chuàng)建項(xiàng)目時(shí),總會(huì)碰到一些小問題,下面這篇文章主要給大家介紹了關(guān)于idea構(gòu)建web項(xiàng)目的超級(jí)詳細(xì)教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • Compare And Swap底層原理及代碼示例詳解

    Compare And Swap底層原理及代碼示例詳解

    這篇文章主要介紹了Compare And Swap底層原理及代碼示例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java之CMS和G1垃圾回收過程的異同說明

    Java之CMS和G1垃圾回收過程的異同說明

    CMS垃圾回收器主要基于并發(fā)-清理算法,目的是減少停頓時(shí)間,通過四個(gè)主要階段進(jìn)行垃圾回收:初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記和并發(fā)清理,G1垃圾回收器采用標(biāo)記-整理算法,是JDK9后的默認(rèn)垃圾收集器,設(shè)計(jì)為全功能全代收集器
    2024-09-09
  • 項(xiàng)目中SpringBoot配置文件未生效的原因分析與解決方法

    項(xiàng)目中SpringBoot配置文件未生效的原因分析與解決方法

    在日常開發(fā) Spring Boot 項(xiàng)目的時(shí)候,大家應(yīng)該都遇到過這樣一種情況:明明寫了 application.yml,但是項(xiàng)目跑起來之后,配置好像完全沒被加載,下面我們就來聊聊Spring Boot 配置文件未生效 的幾個(gè)典型場景與解決方法吧
    2025-09-09
  • Java求一個(gè)分?jǐn)?shù)數(shù)列的前20項(xiàng)之和的實(shí)現(xiàn)代碼

    Java求一個(gè)分?jǐn)?shù)數(shù)列的前20項(xiàng)之和的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Java求一個(gè)分?jǐn)?shù)數(shù)列的前20項(xiàng)之和的實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2017-02-02
  • 詳解Spring Boot 自定義PropertySourceLoader

    詳解Spring Boot 自定義PropertySourceLoader

    這篇文章主要介紹了詳解Spring Boot 自定義PropertySourceLoader,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • 使用IDEA反編譯沒有擦除泛型的原因解析

    使用IDEA反編譯沒有擦除泛型的原因解析

    Java泛型引入至今已有近20年,“偽泛型”已被認(rèn)為是所有開發(fā)者的共識(shí),沒有必要再在反編譯后體現(xiàn)出來反倒大大降低了可讀性,這篇文章主要介紹了使用IDEA反編譯沒有擦除泛型的原因解析,需要的朋友可以參考下
    2023-05-05
  • 麒麟OS?(ARM64)?安裝MySQL、Redis、JDK、Docker超全指南

    麒麟OS?(ARM64)?安裝MySQL、Redis、JDK、Docker超全指南

    麒麟ARM操作系統(tǒng)是國企和政務(wù)機(jī)關(guān)推行信創(chuàng)化選擇率比較高的一款操作系統(tǒng),然而ARM操作系統(tǒng)非主流的X86系統(tǒng),除了命令一樣,在架構(gòu)方面差別極大,這篇文章主要介紹了麒麟OS?(ARM64)?安裝MySQL、Redis、JDK、Docker的相關(guān)資料,需要的朋友可以參考下
    2025-09-09
  • Spring整合Weblogic jms實(shí)例詳解

    Spring整合Weblogic jms實(shí)例詳解

    這篇文章主要介紹了Spring整合Weblogic jms實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • SpringBoot結(jié)果封裝和異常攔截的實(shí)現(xiàn)示例

    SpringBoot結(jié)果封裝和異常攔截的實(shí)現(xiàn)示例

    SpringBoot 項(xiàng)目中,我們通常需要將結(jié)果數(shù)據(jù)封裝成特定的格式,以方便客戶端進(jìn)行處理,本文主要介紹了SpringBoot?優(yōu)雅的結(jié)果封裝和異常攔截,感興趣的可以了解一下
    2023-08-08

最新評(píng)論

法库县| 龙陵县| 忻州市| 武宣县| 宜兰县| 滨州市| 沁阳市| 当阳市| 平安县| 灵山县| 南华县| 天镇县| 兖州市| 封开县| 自治县| 云阳县| 花莲县| 获嘉县| 朝阳县| 克什克腾旗| 临西县| 南昌县| 凤台县| 江西省| 乐东| 拉孜县| 鹤峰县| 搜索| 靖江市| 西林县| 佛学| 万安县| 苍山县| 桐柏县| 霍城县| 武威市| 静宁县| 南江县| 烟台市| 阿坝| 达拉特旗|