Spring Boot高并發 鎖的使用方法

Spring Boot高并發 鎖的使用方法


在高并發場景中(比如電商秒殺、搶票系統、轉賬交易),多個線程/用戶會同時操作同一共享資源(如庫存、賬戶余額、訂單號)。如果不做控制,會導致數據錯誤(如庫存超賣、余額負數)、業務邏輯混亂(如重復下單)。鎖(Lock)是解決這類問題的核心工具之一。

一、概述:為什么高并發下需要鎖?

1. 高并發的“數據競爭”問題

當多個線程同時修改同一個共享資源時(如數據庫的庫存字段、內存中的緩存值),如果沒有控制,會出現“數據不一致”。例如:

  • 電商場景:商品庫存剩余10件,用戶A和用戶B同時下單,兩個線程同時讀取到庫存為10,都扣減1后寫回9,最終庫存變成9(實際應賣出2件,庫存應為8)。
  • 轉賬場景:用戶賬戶余額100元,同時發起兩筆50元轉賬,兩個線程都讀到余額100,都扣減50后寫回50,最終余額變成50(實際應扣減100,余額0)。
2. 鎖的核心作用

鎖是一種“互斥機制”,保證同一時刻只有一個線程能操作共享資源,避免數據競爭。類比現實中的“公共衛生間”:鎖門后,其他人必須等待,直到當前用戶釋放鎖(開門)。

二、鎖的類型與適用場景

在Spring Boot中,常用的鎖分為3類,需根據業務場景選擇:

鎖類型實現方式適用場景優點缺點
JVM內置鎖synchronized關鍵字單體應用(單進程)的小范圍并發代碼簡單,JVM自動管理鎖無法跨進程(分布式場景無效)
JUC顯式鎖ReentrantLock(Lock接口)單體應用需要靈活控制鎖(如超時、可中斷)支持超時、可中斷、公平鎖需要手動釋放鎖(否則死鎖)
分布式鎖Redis(Redisson)、ZooKeeper分布式系統(多進程/多服務器)的并發跨進程協調,全局唯一依賴外部組件(如Redis),有性能開銷

三、鎖的具體使用與代碼實現

場景說明:模擬“電商庫存扣減”

需求:用戶下單時扣減商品庫存,要求高并發下庫存不能超賣(庫存≥0)。
假設商品ID為1001,初始庫存10件。

1. JVM內置鎖:synchronized

適用于單體應用(只有1個Spring Boot實例),代碼簡單,JVM自動加鎖/釋放。

@Service
public class StockService {// 模擬數據庫中的庫存(實際開發中用數據庫或緩存)private int stock = 10;// 下單扣減庫存(synchronized保證同一時刻只有1個線程執行)public synchronized boolean deductStock(int productId, int count) {// 檢查庫存是否足夠if (stock < count) {return false; // 庫存不足}// 模擬業務耗時(如查詢數據庫、記錄日志)try {Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();}// 扣減庫存stock -= count;System.out.println("扣減成功,剩余庫存:" + stock);return true;}
}
關鍵說明
  • synchronized修飾方法時,鎖的是當前對象(this);若修飾靜態方法,鎖的是類(StockService.class)。
  • 缺點:無法跨進程(如果部署多個Spring Boot實例,每個實例的stock是獨立的,鎖無效)。
2. JUC顯式鎖:ReentrantLock

適用于單體應用,但需要更靈活的鎖控制(如設置超時、可中斷)。

@Service
public class StockService {private int stock = 10;// 顯式鎖(可重入鎖,支持公平/非公平)private final ReentrantLock lock = new ReentrantLock();public boolean deductStock(int productId, int count) {// 嘗試加鎖(最多等待2秒,避免死鎖)try {if (lock.tryLock(2, TimeUnit.SECONDS)) {if (stock >= count) {Thread.sleep(100); // 模擬業務耗時stock -= count;System.out.println("扣減成功,剩余庫存:" + stock);return true;} else {System.out.println("庫存不足");return false;}} else {System.out.println("獲取鎖超時");return false;}} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;} finally {// 必須在finally中釋放鎖(避免異常導致鎖未釋放)lock.unlock();}}
}
關鍵說明
  • tryLock(timeout, unit):嘗試加鎖,超時未獲取則放棄(避免線程無限等待)。
  • finally中釋放鎖:必須手動釋放,否則其他線程永遠無法獲取鎖(死鎖)。
  • 優點:比synchronized靈活(支持超時、可中斷),適合復雜業務邏輯。
3. 分布式鎖:Redisson(基于Redis)

適用于分布式系統(多個Spring Boot實例部署),解決跨進程的并發問題。

@Service
public class StockService {@Autowiredprivate RedissonClient redissonClient;// 模擬數據庫庫存(實際用數據庫或緩存,如Redis存儲庫存)private int stock = 10;public boolean deductStock(int productId, int count) {// 定義鎖的名稱(按商品ID隔離,不同商品用不同鎖)String lockKey = "lock:product:" + productId;RLock lock = redissonClient.getLock(lockKey);try {// 加鎖(自動續期,防止業務耗時過長鎖過期)// waitTime: 等待鎖的最大時間(5秒)// leaseTime: 鎖自動釋放時間(30秒,防止死鎖)boolean locked = lock.tryLock(5, 30, TimeUnit.SECONDS);if (!locked) {System.out.println("獲取鎖失敗,稍后再試");return false;}// 檢查并扣減庫存if (stock >= count) {Thread.sleep(100); // 模擬業務耗時stock -= count;System.out.println("扣減成功,剩余庫存:" + stock);return true;} else {System.out.println("庫存不足");return false;}} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;} finally {// 釋放鎖(只有自己加的鎖才能釋放)if (lock.isHeldByCurrentThread()) {lock.unlock();}}}
}
關鍵說明
  • 鎖名稱:用lock:product:1001隔離不同商品,避免不同商品的庫存操作互相阻塞。
  • 自動續期:Redisson默認會為鎖“續期”(每10秒續30秒),防止業務邏輯未執行完鎖就過期(比如扣庫存需要20秒,鎖30秒過期,續期避免提前釋放)。
  • 分布式場景有效性:多個Spring Boot實例通過Redis的lockKey協調,同一時刻只有1個實例能獲取鎖,避免跨進程的庫存超賣。

四、實際業務舉例:電商秒殺場景

場景描述

某商品開啟秒殺(庫存100件),1000個用戶同時點擊“立即購買”,需要保證:

  • 只有前100個用戶能成功購買(庫存不超賣)。
  • 后續用戶提示“已售罄”。
解決方案(分布式鎖)
  1. 用戶點擊下單時,先通過Redisson獲取該商品的分布式鎖(lock:seckill:productId)。
  2. 獲得鎖的線程檢查庫存是否足夠(stock > 0)。
  3. 庫存足夠則扣減庫存,生成訂單;否則返回“已售罄”。
  4. 釋放鎖,讓其他線程繼續競爭。
關鍵點
  • 鎖粒度:按商品ID加鎖(如lock:seckill:1001),不同商品的秒殺互不影響,提升并發效率。
  • 防死鎖:設置鎖的自動釋放時間(如30秒),即使業務異常未釋放鎖,鎖也會自動過期。
  • 性能優化:庫存可存儲在Redis中(GET/SET操作比數據庫快),減少數據庫壓力。

五、總結

1. 鎖的選擇原則
  • 單體應用:優先用synchronized(簡單)或ReentrantLock(需要靈活控制)。
  • 分布式系統:必須用分布式鎖(如Redisson),避免跨進程數據競爭。
2. 注意事項
  • 鎖粒度:盡量縮小鎖的范圍(只鎖共享資源的操作代碼),避免“鎖整個方法”降低性能。
  • 防死鎖:設置鎖的超時時間(如tryLock(5, 30, TimeUnit.SECONDS)),避免線程無限等待。
  • 性能權衡:鎖會降低并發吞吐量(同一時刻只有1個線程操作),需結合業務場景(如秒殺允許少量延遲)。
3. 擴展思考
  • 無鎖方案:對于簡單計數(如訪問量),可用AtomicInteger(基于CAS無鎖操作),但無法解決復雜業務邏輯(如庫存扣減+訂單生成)。
  • 讀寫鎖:讀多寫少場景(如商品詳情頁緩存),可用ReentrantReadWriteLock(允許多個讀鎖并發,寫鎖互斥)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86923.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86923.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86923.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

二十九:Dynamic Prompts插件動態提示詞講解

引言:可變化提示詞,隨機抽取不固定 使用方式一:{提示詞1|提示詞2|。。。。}------從提示詞種隨機抽取生成 方式二:{25::提示詞1|75::提示詞2}------數字為每個提示詞的占比,相當于權重 方式三:{2$$提示詞1|提示詞2|提示詞3|提示詞4|。。。}從中選區2個搭配生成(可以換 比…

vscode 改注釋的顏色,默認是灰色的,想改成紅色

修改VScode編輯器默認注釋的顏色_databricks代碼中怎么設置讓注釋是灰色的-CSDN博客 //改變注釋顏色"editor.tokenColorCustomizations": {"comments": "#009933" // 注釋}, //如果后面還加內容&#xff0c;記得塊末用逗號隔開我自己用的vscdoe.…

chili3d筆記22 正交投影3d重建筆記3 面構建

雙視圖重建3d solid import { FaceNode } from "chili"; import {IDocument,IEdge,Logger,ShapeNode,XYZ } from "chili-core"; import { Graph } from "graphlib"; function pointToString(point: XYZ): string {return ${point.x.toFixed(0)}-…

Kotlin 協程使用與通信

一、協程基礎使用 1. 協程的三種創建方式 (1) launch - 啟動后臺作業 val job CoroutineScope(Dispatchers.IO).launch {// 后臺操作delay(1000)println("任務完成 ${Thread.currentThread().name}")// 輸出&#xff1a;任務完成 DefaultDispatcher-worker-1 } j…

Ubuntu服務器(公網)- Ubuntu客戶端(內網)的FRP內網穿透配置教程

以下是為Ubuntu服務器&#xff08;公網&#xff09;- Ubuntu客戶端&#xff08;內網&#xff09;的FRP內網穿透配置教程&#xff0c;基于最新版本&#xff08;2025年6月&#xff0c;使用frp_0.61.1_linux_amd64&#xff09;整理&#xff1a; 一、服務端配置&#xff08;公網Ubu…

什么是哈希函數(SHA-256)

SHA-256 是區塊鏈系統中最核心的加密基礎之一&#xff0c;尤其是在比特幣、以太坊、文件存證等場景中扮演“指紋識別器”的角色。下面是對它的詳細講解&#xff0c;包括原理、特點、用途和代碼示例。 &#x1f4cc; 一、什么是 SHA-256&#xff1f; SHA-256 是一種密碼學哈希函…

大模型的“Tomcat”:一文讀懂AI推理引擎(Inference Engine)

點擊下方“JavaEdge”&#xff0c;選擇“設為星標” 第一時間關注技術干貨&#xff01; 免責聲明~ 任何文章不要過度深思&#xff01; 萬事萬物都經不起審視&#xff0c;因為世上沒有同樣的成長環境&#xff0c;也沒有同樣的認知水平&#xff0c;更「沒有適用于所有人的解決方案…

《從0到1:C/C++音視頻開發自學完全指南》

從0到1&#xff1a;C/C音視頻開發自學完全指南 一、開篇&#xff1a;為什么選擇C/C切入音視頻開發&#xff1f; 當你刷著抖音短視頻、參加騰訊會議、觀看B站直播時&#xff0c;背后都是音視頻技術在支撐。根據艾瑞咨詢數據&#xff0c;2024年中國音視頻相關產業規模已突破5000…

微信小程序之單行溢出隱藏和雙行溢出隱藏

首先&#xff0c;我們做個text&#xff0c;加入了一個長文本&#xff0c;就像下面那樣&#xff1a; wxml : <view class"container"><text>劉德華&#xff08;Andy Lau&#xff09;&#xff0c;1961年9月27日出生于中國香港&#xff0c;華語影視男演員、…

PHP安裝使用教程

一、PHP 簡介 PHP&#xff08;Hypertext Preprocessor&#xff09;是一種廣泛應用的開源服務器端腳本語言&#xff0c;尤其適用于 Web 開發&#xff0c;可嵌入 HTML 中使用。其運行速度快、易學易用&#xff0c;支持多種數據庫和平臺。 二、PHP 安裝教程 2.1 支持平臺 PHP 支…

ThreadLocal、InheritableThreadLocal與TransmittableThreadLocal深度解析

文章目錄 一、概念說明1、ThreadLocal2、InheritableThreadLocal3、TransmittableThreadLocal 二、使用場景1、ThreadLocal2、InheritableThreadLocal3、TransmittableThreadLocal 三、存在的問題1、ThreadLocal2、InheritableThreadLocal3、TransmittableThreadLocal 四、示例…

ERP系統Bug記錄

2025.06.30 2025/06/30-10:51:02 [http-nio-9999-exec-3] com.yxx.jsh.erp.service.LogService - 異常碼[300],異常提示[數據查詢異常],異常[{}] java.lang.NullPointerException: nullat com.yxx.jsh.erp.base.TableSupport.getBuildPageRequest(TableSupport.java:46)at com…

C# Avalonia 的 Source Generators 用處

C# Avalonia 的 Source Generators 用處 文章目錄 **1. 自動生成 MVVM 綁定代碼****2. 強類型 XAML 數據綁定****3. 自動注冊視圖&#xff08;View&#xff09;與視圖模型&#xff08;ViewModel&#xff09;****4. 資源文件與本地化的強類型訪問****5. 路由事件與命令的自動化處…

stm32之測量占空比

#include "tim4.h"void TIM4_Init(void) {// 開啟時鐘RCC->APB1ENR | RCC_APB1ENR_TIM4EN;RCC->APB2ENR | RCC_APB2ENR_IOPBEN; // 使用 TIM4 的 GPIOB 時鐘// 配置 PB6 為浮空輸入 CNF 01 MODE 00GPIOB->CRL & ~GPIO_CRL_MODE6;GPIOB->CRL & ~G…

工廠模式 - Flutter中的UI組件工廠,按需生產各種“產品

想要動態創建不同風格的按鈕&#xff1f;想一鍵切換整個主題&#xff1f;工廠模式就是你的"生產流水線"&#xff01; 想象一下這個場景&#xff1a; 你決定擴大奶茶店業務&#xff0c;推出兩個品牌系列&#xff1a; 經典系列&#xff1a;傳統珍珠奶茶&#xff0c;紅…

基于 SpringBoot+Vue.js+ElementUI 的 Cosplay 論壇設計與實現7000字論文

基于 SpringBootVue.jsElementUI 的 Cosplay 論壇設計與實現 摘要 本論文設計并實現了一個基于 SpringBoot、Vue.js 和 ElementUI 的 Cosplay 論壇平臺。該平臺旨在為 Cosplay 愛好者提供一個集作品展示、交流互動、活動組織于一體的綜合性社區。論文首先分析了 Cosplay 論壇…

超標量處理器11-Alpha21264 處理器

1. 簡介 21264處理器是一款4-way&#xff0c;亂序執行的超標量處理器&#xff0c;采用0.35um的CMOS工藝&#xff0c;工作電壓是2.2V, 工作頻率是466-667MHz; 處理器能支持60條指令&#xff0c;也即ROB的深度是60; Load/Store指令也采取亂序執行, 總共7級流水。I-CACHE和D-CACH…

Spring 中 Bean 的生命周期

一、什么是 Bean 生命周期&#xff1f; Spring 中的 Bean 生命周期是指一個 Bean 從 被容器創建到 最終銷毀 所經歷的一系列過程。 它體現了 Spring IOC 容器在管理 Bean 實例時所執行的各個鉤子流程&#xff0c;包括初始化、依賴注入、增強處理、銷毀等多個環節。 二、Bean 生…

C++ 中 std::string 與 QString 的深度剖析

在 C 編程領域&#xff0c;std::string 和 QString 是兩種廣泛應用的字符串類型&#xff0c;它們在設計理念、功能特性以及適用場景上都呈現出鮮明的特點。本文將從多個維度對這兩種字符串類型進行深度剖析&#xff0c;并詳細闡述它們之間的相互轉化方法。 std::string 是 C 標…

不止于“修補”:我如何用Adobe AI重塑設計與視頻敘事流程

最近我深度體驗了一把來自英國Parvis School of Economics and Music的Adobe正版教育訂閱&#xff0c;在把玩PhotoShop、Premiere Pro這些“老伙計”的新功能時&#xff0c;的確挖到了一些寶藏&#xff0c;覺得非常有必要與大家說道說道。首先得聊聊這個訂閱給我的直觀感受&…