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

Spring使用注解解決線程并發(fā)問題的多種方案

 更新時間:2026年03月12日 08:48:19   作者:xiaoye3708  
在?Spring?中,線程并發(fā)問題的核心是共享資源的競爭,注解本身不能直接解決并發(fā)問題,但?Spring?提供了配套注解結合并發(fā)編程規(guī)范,可以優(yōu)雅地實現(xiàn)線程安全控制,下面我會從最常用的場景出發(fā),講解?Spring?中如何通過注解解決并發(fā)問題,需要的朋友可以參考下

引言

在 Spring 中,線程并發(fā)問題的核心是共享資源的競爭(如多線程讀寫同一變量、并發(fā)操作數(shù)據(jù)庫等),注解本身不能直接解決并發(fā)問題,但 Spring 提供了配套注解結合并發(fā)編程規(guī)范,可以優(yōu)雅地實現(xiàn)線程安全控制。

下面我會從最常用的場景出發(fā),講解 Spring 中如何通過注解解決并發(fā)問題,包括方法級別的同步控制、分布式鎖(解決多實例并發(fā))等核心方案。

一、基礎場景:單實例下的方法同步(@Synchronized 或 @Lock)

Spring 本身沒有內置的 @Synchronized 注解,但可以通過 AOP 自定義注解 實現(xiàn)方法級別的同步鎖,替代原生 synchronized 關鍵字,讓代碼更優(yōu)雅。

1. 自定義同步注解 + AOP 實現(xiàn)

步驟 1:定義同步注解

import java.lang.annotation.*;
/**
 * 自定義同步注解,用于標記需要同步的方法
 */
@Target(ElementType.METHOD) // 僅作用于方法
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodSynchronized {
    // 可指定鎖的名稱(可選)
    String lockKey() default "";
}

步驟 2:實現(xiàn) AOP 切面(核心)

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 同步注解的切面實現(xiàn)
 */
@Aspect
@Component // 交給 Spring 管理
public class SynchronizedAspect {
    // 維護鎖的緩存,避免重復創(chuàng)建鎖
    private final ConcurrentHashMap<String, Lock> lockMap = new ConcurrentHashMap<>();
    /**
     * 環(huán)繞通知:攔截所有標注了 @MethodSynchronized 的方法
     */
    @Around("@annotation(methodSynchronized)")
    public Object around(ProceedingJoinPoint joinPoint, MethodSynchronized methodSynchronized) throws Throwable {
        // 1. 確定鎖的 key(默認用方法全限定名,也可自定義)
        String lockKey = methodSynchronized.lockKey().isEmpty() 
                ? joinPoint.getSignature().toLongString() 
                : methodSynchronized.lockKey();
        // 2. 獲取或創(chuàng)建鎖(ConcurrentHashMap 保證線程安全)
        Lock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock());
        try {
            lock.lock(); // 加鎖
            return joinPoint.proceed(); // 執(zhí)行原方法
        } finally {
            lock.unlock(); // 釋放鎖(finally 確保鎖一定會釋放)
        }
    }
}

步驟 3:使用注解解決并發(fā)問題

import org.springframework.stereotype.Service;
@Service
public class OrderService {
    // 共享資源:模擬庫存
    private int stock = 100;
    /**
     * 扣減庫存(并發(fā)場景)
     * 使用 @MethodSynchronized 保證方法執(zhí)行的原子性
     */
    @MethodSynchronized
    public void deductStock(int num) {
        if (stock >= num) {
            // 模擬耗時操作(如數(shù)據(jù)庫查詢)
            try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
            stock -= num;
            System.out.println("扣減成功,剩余庫存:" + stock);
        } else {
            System.out.println("庫存不足");
        }
    }
    // 獲取庫存(測試用)
    public int getStock() {
        return stock;
    }
}

步驟 4:測試并發(fā)效果

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestConcurrent {
    public static void main(String[] args) {
        // 初始化 Spring 容器
        AnnotationConfigApplicationContext context = 
                new AnnotationConfigApplicationContext("com.example");
        OrderService orderService = context.getBean(OrderService.class);
        // 模擬 10 個線程并發(fā)扣減庫存(每個扣減 10)
        for (int i = 0; i < 10; i++) {
            new Thread(() -> orderService.deductStock(10)).start();
        }
    }
}

輸出結果(無并發(fā)問題,庫存最終為 0):

扣減成功,剩余庫存:90
扣減成功,剩余庫存:80
...
扣減成功,剩余庫存:0

2. 進階:按參數(shù)加鎖(解決細粒度并發(fā))

如果希望僅對相同參數(shù)的請求加鎖(比如同一訂單號的并發(fā)操作),可以修改 AOP 切面,將參數(shù)納入鎖的 key:

// 修改 SynchronizedAspect 的 around 方法
String lockKey = methodSynchronized.lockKey().isEmpty() 
        ? joinPoint.getSignature().toLongString() + Arrays.toString(joinPoint.getArgs()) 
        : methodSynchronized.lockKey() + Arrays.toString(joinPoint.getArgs());

二、分布式場景:多實例并發(fā)(@RedissonLock)

如果你的應用部署在多臺服務器(多實例),本地鎖(如 ReentrantLock、synchronized)會失效,此時需要用分布式鎖,Spring 中可結合 Redis(Redisson)實現(xiàn)注解式分布式鎖。

1. 依賴引入(Maven)

<!-- Redisson 分布式鎖核心依賴 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.3</version>
</dependency>

2. 自定義分布式鎖注解

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedissonLock {
    // 鎖的 key(支持 SpEL 表達式,如 "#orderId")
    String key();
    // 鎖的過期時間(防止死鎖)
    long expireTime() default 30;
    // 時間單位
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

3. AOP 實現(xiàn)分布式鎖切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
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 java.lang.reflect.Method;
@Aspect
@Component
public class RedissonLockAspect {
    @Autowired
    private RedissonClient redissonClient;
    // SpEL 表達式解析器
    private final ExpressionParser parser = new SpelExpressionParser();
    // 參數(shù)名解析器(獲取方法參數(shù)名)
    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    @Around("@annotation(redissonLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedissonLock redissonLock) throws Throwable {
        // 1. 解析 SpEL 表達式,獲取鎖的 key
        String key = parseSpEL(joinPoint, redissonLock.key());
        // 2. 獲取分布式鎖
        RLock lock = redissonClient.getLock(key);
        try {
            // 3. 加鎖(支持過期時間,防止死鎖)
            boolean locked = lock.tryLock(0, redissonLock.expireTime(), redissonLock.timeUnit());
            if (!locked) {
                throw new RuntimeException("獲取分布式鎖失敗,請稍后重試");
            }
            // 4. 執(zhí)行原方法
            return joinPoint.proceed();
        } finally {
            // 5. 釋放鎖(僅當前線程持有鎖時釋放)
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    // 解析 SpEL 表達式
    private String parseSpEL(ProceedingJoinPoint joinPoint, String spEL) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 獲取方法參數(shù)名
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
        // 構建 SpEL 上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        // 解析表達式
        return parser.parseExpression(spEL).getValue(context, String.class);
    }
}

4. 使用分布式鎖注解

@Service
public class OrderService {
    /**
     * 提交訂單(分布式并發(fā)場景)
     * @param orderId 訂單號(按訂單號加鎖,不同訂單不互斥)
     */
    @RedissonLock(key = "'order:' + #orderId") // SpEL 表達式,最終 key 為 "order:123456"
    public void submitOrder(String orderId) {
        // 并發(fā)操作:如扣減庫存、生成訂單
        System.out.println("訂單 " + orderId + " 提交成功");
    }
}

三、其他補充方案

Spring 事務注解(@Transactional):雖然 @Transactional 主要用于事務管理,但它的隔離級別(如 Isolation.REPEATABLE_READ)可以解決數(shù)據(jù)庫層面的并發(fā)問題(如臟讀、不可重復讀)。例如:

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void updateUserBalance(Long userId, BigDecimal amount) {
    // 數(shù)據(jù)庫并發(fā)更新操作
}

Spring 緩存注解(@Cacheable):對于讀多寫少的場景,@Cacheable 可以將查詢結果緩存到 Redis 等介質,減少數(shù)據(jù)庫的并發(fā)訪問,間接降低并發(fā)壓力。

總結

  1. 單實例并發(fā):通過自定義 @MethodSynchronized 注解 + AOP 實現(xiàn)方法級同步鎖,替代原生 synchronized,支持細粒度(按參數(shù))加鎖。
  2. 分布式并發(fā):結合 Redisson 實現(xiàn) @RedissonLock 注解,通過 Redis 分布式鎖解決多實例的并發(fā)問題,核心是 “鎖的 key 唯一性 + 自動過期防死鎖”。
  3. 數(shù)據(jù)庫層并發(fā):使用 @Transactional 配置合適的隔離級別,解決數(shù)據(jù)庫層面的并發(fā)問題;讀多寫少場景可結合 @Cacheable 減少并發(fā)壓力。

核心原則:注解是 “語法糖”,真正解決并發(fā)的是鎖機制(本地鎖 / 分布式鎖)和原子性保證,注解只是讓鎖的使用更優(yōu)雅、更易維護。

以上就是Spring使用注解解決線程并發(fā)問題的多種方案的詳細內容,更多關于Spring注解解決線程并發(fā)的資料請關注腳本之家其它相關文章!

相關文章

  • MyBatis中的collection兩種使用方法及效率比較

    MyBatis中的collection兩種使用方法及效率比較

    collection主要是應對表關系是一對多的情況,本文主要介紹了MyBatis中的collection兩種使用方法及效率比較,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • 為什么Java單例模式一定要加?volatile

    為什么Java單例模式一定要加?volatile

    這篇文章主要介紹了為什么Java單例一定要加volatile,指的是為什么懶漢模式中的私有變量要加volatile?帶著疑問一起學習下面文章內容吧
    2022-05-05
  • 如何實現(xiàn)Java監(jiān)聽器詳解

    如何實現(xiàn)Java監(jiān)聽器詳解

    今天帶大家了解Java監(jiān)聽器是如何實現(xiàn)的及實現(xiàn)原理是什么,文中有非常詳細的說明,對正在學習的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • Java Lambda表達式和函數(shù)式接口實例分析

    Java Lambda表達式和函數(shù)式接口實例分析

    這篇文章主要介紹了Java Lambda表達式和函數(shù)式接口,結合實例形式分析了Java8 Lambda表達式和函數(shù)式接口相關原理、用法及操作注意事項,需要的朋友可以參考下
    2019-09-09
  • Springboot服務引用Nacos中新增的配置文件失敗問題及解決

    Springboot服務引用Nacos中新增的配置文件失敗問題及解決

    這篇文章主要介紹了Springboot服務引用Nacos中新增的配置文件失敗問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • Java四種訪問控制修飾符知識點總結

    Java四種訪問控制修飾符知識點總結

    本篇文章給大家詳細分析了Java四種訪問控制修飾符的相關知識點,有興趣的朋友可以參考學習下。
    2018-03-03
  • 創(chuàng)建自定義的Java注解類的方法

    創(chuàng)建自定義的Java注解類的方法

    這篇文章主要介紹了創(chuàng)建自定義的Java注解類的方法,主要部分為創(chuàng)建一個帶有Java類型@interface的新類型,需要的朋友可以參考下
    2015-07-07
  • 用Java實現(xiàn)連連看小游戲

    用Java實現(xiàn)連連看小游戲

    這篇文章主要為大家詳細介紹了用Java實現(xiàn)連連看小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java實現(xiàn)獲取銀行卡所屬銀行,驗證銀行卡號是否正確的方法詳解

    Java實現(xiàn)獲取銀行卡所屬銀行,驗證銀行卡號是否正確的方法詳解

    這篇文章主要介紹了Java實現(xiàn)獲取銀行卡所屬銀行,驗證銀行卡號是否正確的方法,結合實例形式詳細分析了java判斷銀行卡歸屬地及有效性的原理與相關實現(xiàn)技巧,需要的朋友可以參考下
    2019-09-09
  • Java生成壓縮文件的實例代碼

    Java生成壓縮文件的實例代碼

    在工作過程中,需要將一個文件夾生成壓縮文件,然后提供給用戶下載。下面通過實例代碼給大家介紹Java生成壓縮文件的方法,感興趣的朋友一起看看吧
    2018-06-06

最新評論

奉化市| 景洪市| 丰县| 平罗县| 高尔夫| 玉屏| 盘锦市| 汝州市| 京山县| 于都县| 蓬莱市| 石楼县| 弥勒县| 丹江口市| 天长市| 盈江县| 古丈县| 吕梁市| 延长县| 黄平县| 思南县| 松江区| 邢台市| 洪雅县| 通化市| 基隆市| 边坝县| 丹江口市| 香港| 阿拉尔市| 逊克县| 安平县| 菏泽市| 新郑市| 临洮县| 繁峙县| 柘荣县| 黄龙县| 平定县| 鸡西市| 泰兴市|