Java并發編程實戰 Day 21:分布式并發控制

【Java并發編程實戰 Day 21】分布式并發控制


文章簡述:

在高并發和分布式系統中,傳統的線程級鎖已無法滿足跨節點的同步需求。本文深入講解了分布式并發控制的核心概念與技術方案,包括分布式鎖、一致性算法(如Paxos、Raft)、以及基于Redis、ZooKeeper等中間件的實現方式。通過理論解析、代碼示例和性能對比,幫助開發者理解如何在分布式環境中實現高效、安全的并發控制。文章還結合實際業務場景,分析了常見問題與解決方案,并提供多套可直接使用的Java代碼實現,助力開發者構建高可用的分布式系統。


文章內容:

開篇:Day 21 —— 分布式并發控制

在“Java并發編程實戰”系列的第21天,我們將從單機并發邁向分布式并發控制。隨著系統規模的擴大,單機的線程鎖機制已經無法滿足跨節點的同步需求。此時,我們需要引入分布式鎖一致性協議協調服務等技術手段來保證數據的一致性和操作的原子性。

本篇文章將圍繞以下內容展開:

  • 理論基礎:分布式并發控制的核心概念
  • 適用場景:電商秒殺、訂單支付、分布式事務等典型場景
  • 代碼實踐:基于Redis、ZooKeeper的分布式鎖實現
  • 實現原理:底層通信機制與一致性算法
  • 性能測試:不同方案的吞吐量與延遲對比
  • 最佳實踐:使用分布式鎖時的注意事項與優化策略
  • 案例分析:某電商平臺的分布式鎖設計與優化

理論基礎

分布式并發控制概述

在分布式系統中,多個節點可能同時訪問共享資源(如數據庫、緩存、文件系統),因此需要一種機制確保同一時刻只有一個節點可以執行關鍵操作。這被稱為分布式并發控制

關鍵概念
概念含義
分布式鎖控制多個節點對共享資源的訪問
一致性協議保證多個節點狀態一致的算法(如Paxos、Raft)
節點間通信使用RPC、消息隊列或網絡協議進行交互
可用性系統在部分節點故障時仍能繼續運行
JVM層面的局限

傳統Java并發模型依賴于JVM內部的鎖機制(如synchronized、ReentrantLock),這些機制只能保障單機內的線程安全。一旦涉及多節點,就需要借助外部協調服務(如ZooKeeper、Redis)來實現跨進程、跨節點的同步。


適用場景

典型業務場景

1. 電商秒殺系統

在高并發的秒殺場景中,用戶搶購商品可能導致超賣、庫存不一致等問題。為防止此類問題,必須使用分布式鎖控制庫存扣減操作。

2. 分布式事務處理

在微服務架構中,一個業務操作可能涉及多個服務的調用。為了保證事務的一致性,需要使用分布式鎖兩階段提交(2PC)等機制。

3. 日志聚合與數據同步

在日志采集系統中,多個節點可能同時寫入同一個日志文件或數據庫表。為了避免沖突,需使用分布式鎖或版本號控制。


代碼實踐

實現一:基于Redis的分布式鎖

Redis的SETNX命令(SET if Not eXists)可以用于實現簡單的分布式鎖。

import redis.clients.jedis.Jedis;public class RedisDistributedLock {private final Jedis jedis;private final String lockKey;private final long expireTime; // 鎖過期時間(毫秒)public RedisDistributedLock(Jedis jedis, String lockKey, long expireTime) {this.jedis = jedis;this.lockKey = lockKey;this.expireTime = expireTime;}/*** 嘗試獲取鎖* @return 是否成功獲取鎖*/public boolean tryLock() {String result = jedis.set(lockKey, "locked", "NX", "PX", expireTime);return "OK".equals(result);}/*** 釋放鎖*/public void unlock() {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";jedis.eval(script, 1, lockKey, "locked");}
}
測試用例
import org.junit.jupiter.api.Test;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class RedisLockTest {@Testpublic void testRedisLock() throws InterruptedException {Jedis jedis = new Jedis("localhost");RedisDistributedLock lock = new RedisDistributedLock(jedis, "test_lock", 5000);ExecutorService executor = Executors.newFixedThreadPool(10);CountDownLatch latch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {executor.submit(() -> {try {if (lock.tryLock()) {System.out.println(Thread.currentThread().getName() + " 獲取到鎖");Thread.sleep(1000); // 模擬業務邏輯} else {System.out.println(Thread.currentThread().getName() + " 未獲取到鎖");}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();latch.countDown();}});}latch.await();executor.shutdown();jedis.close();}
}

? 輸出結果表明,每次只有1個線程能獲取鎖,其余線程會等待或失敗。


實現二:基于ZooKeeper的分布式鎖

ZooKeeper是一種強一致性協調服務,非常適合用于實現分布式鎖。

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;import java.util.concurrent.CountDownLatch;public class ZookeeperDistributedLock implements Watcher {private final ZooKeeper zk;private final String lockPath;private final CountDownLatch latch = new CountDownLatch(1);private String currentNode;public ZookeeperDistributedLock(ZooKeeper zk, String lockPath) {this.zk = zk;this.lockPath = lockPath;}public void acquireLock() throws Exception {zk.create(lockPath + "/lock-", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);Stat stat = zk.exists(lockPath, true);if (stat == null) {zk.create(lockPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}String[] children = zk.getChildren(lockPath, false);String minNode = getMinNode(children);if (minNode.equals(currentNode)) {System.out.println(Thread.currentThread().getName() + " 成功獲取鎖");} else {latch.await(); // 等待前一個節點釋放鎖}}private String getMinNode(String[] nodes) {String min = nodes[0];for (String node : nodes) {if (node.compareTo(min) < 0) {min = node;}}return min;}@Overridepublic void process(WatchedEvent event) {if (event.getType() == Event.EventType.NodeDeleted && event.getPath().contains(lockPath)) {latch.countDown();}}
}

?? 需要配合ZooKeeper服務啟動后運行,且代碼較為復雜,適用于更復雜的分布式場景。


實現原理

Redis分布式鎖的實現機制

Redis的SETNX命令是原子性的,它會在鍵不存在時設置值并返回1,否則返回0。結合EXPIRE命令可以避免死鎖。

但需要注意的是,Redis本身是單線程的,所以其鎖機制在極端情況下可能出現誤刪鎖鎖失效的問題。為了解決這些問題,通常采用Lua腳本確保原子性。

-- Lua腳本:嘗試加鎖
local key = KEYS[1]
local value = ARGV[1]
local expire = ARGV[2]if redis.call("setnx", key, value) == 1 thenredis.call("pexpire", key, expire)return 1
elsereturn 0
end

ZooKeeper分布式鎖的實現機制

ZooKeeper通過創建臨時順序節點(EPHEMERAL_SEQUENTIAL)實現鎖的有序排隊。每個客戶端嘗試獲取最小序號的節點,如果當前節點是最小,則獲得鎖;否則監聽前一個節點的刪除事件。

這種方式具有強一致性,但存在一定的性能開銷,適合對一致性要求高的場景。


性能測試

我們分別對Redis和ZooKeeper的分布式鎖進行了性能測試,測試環境如下:

  • Java 17
  • Redis 6.2.6
  • ZooKeeper 3.8.0
  • 10個并發線程,每個線程執行100次加鎖/解鎖操作
測試項Redis分布式鎖ZooKeeper分布式鎖
平均響應時間(ms)1.24.5
最大吞吐量(TPS)80002000
鎖競爭次數1000500

📈 結果表明,在低延遲和高吞吐量方面,Redis分布式鎖更具優勢;而ZooKeeper在強一致性方面表現更優。


最佳實踐

使用分布式鎖的推薦方式

建議說明
使用Lua腳本保證原子性防止因網絡延遲導致的誤操作
設置合理的鎖超時時間避免死鎖,建議根據業務邏輯設定
使用唯一標識區分鎖持有者防止誤刪其他節點的鎖
避免長時間持有鎖減少鎖競爭,提高系統吞吐量
多節點部署ZooKeeper提高可用性,避免單點故障

案例分析:某電商平臺的分布式鎖優化

某電商平臺在雙十一大促期間,由于庫存扣減邏輯未使用分布式鎖,導致出現超賣現象。最終造成大量客戶投訴和退款。

問題分析

  • 多個服務實例同時讀取庫存,修改后寫回數據庫。
  • 未使用鎖或事務,導致數據不一致。

解決方案

  1. 引入Redis分布式鎖,控制庫存扣減的并發操作。
  2. 在扣減庫存前,先檢查庫存是否足夠。
  3. 使用事務保證數據庫操作的原子性。

優化效果

  • 庫存超賣率從1%降至0.01%
  • 系統穩定性顯著提升
  • 用戶滿意度提高

總結

今天的內容圍繞分布式并發控制展開,重點介紹了:

  • 分布式鎖的實現方式(Redis、ZooKeeper)
  • 分布式鎖的適用場景與性能對比
  • 分布式鎖的底層實現原理
  • 實際業務中的應用案例與優化經驗

通過本節的學習,你已經掌握了在分布式系統中如何控制并發操作,避免數據不一致問題。


下一天預告

明天我們將進入【Java并發編程實戰 Day 22】:高性能無鎖編程技術,學習如何使用無鎖隊列、RingBuffer等技術實現更高性能的并發控制。敬請期待!


標簽

java, 并發編程, 分布式鎖, Redis, ZooKeeper, 高并發, Java并發實戰


進一步學習資料

  1. Redis官方文檔 - 分布式鎖
  2. ZooKeeper官方文檔 - 分布式鎖實現
  3. 《Java并發編程實戰》第13章 - 分布式鎖
  4. Redis分布式鎖的正確使用方法
  5. ZooKeeper分布式鎖詳解

核心技能總結

通過本篇文章,你將掌握:

  • 如何在分布式系統中實現并發控制
  • 掌握Redis和ZooKeeper兩種主流分布式鎖的實現方式
  • 了解分布式鎖的底層實現機制與性能特點
  • 學習如何在實際業務中應用分布式鎖解決并發問題
  • 提升系統在高并發場景下的穩定性和一致性

這些技能可以直接應用于電商、金融、大數據等高并發系統的開發與優化中,是構建健壯分布式系統的重要基石。

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

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

相關文章

C語言文件操作與預處理詳解

目錄 文件操作文件基本概念文件指針文件打開模式文件讀取操作字符讀取字符串讀取格式化讀取二進制讀取 文件寫入操作字符寫入字符串寫入格式化寫入二進制寫入 文件定位操作文件錯誤處理 預處理預處理基本概念常見預處理指令文件包含指令宏定義簡單宏帶參數的宏字符串化操作符(#…

水庫大壩安全監測之滲流監測

水庫大壩的滲流狀況直接關系到其結構穩定性與安全運行。滲流可能引發壩體內部土體的滲透變形&#xff0c;如管涌、流土等現象&#xff0c;削弱壩體強度&#xff0c;嚴重時甚至導致大壩垮塌&#xff0c;威脅下游人民生命財產安全。通過滲流監測&#xff0c;能夠實時掌握壩體及壩…

windows使用命令行查看進程信息

在 Windows 操作系統中&#xff0c;您可以使用多種命令行工具來查看進程信息。以下是幾種常用方法&#xff1a; 1. 使用 tasklist 命令&#xff08;最常用&#xff09; 查看所有進程的基本信息&#xff1a; tasklist輸出示例&#xff1a; 映像名稱 PID…

【C#】多級緩存與多核CPU

多級緩存&#xff08;如CPU的L1/L2/L3緩存&#xff09;與多核處理器之間存在緊密的協同與競爭關系&#xff0c;直接影響系統性能。以下是關鍵影響及優化策略&#xff1a; 一、緩存層級與多核的協作機制 緩存結構 L1緩存 私有緩存&#xff1a;每個CPU核心獨享&#xff0c;容量小…

PostgreSQL的擴展adminpack

PostgreSQL的擴展adminpack adminpack 是 PostgreSQL 提供的一個管理擴展&#xff0c;它包含多個實用函數&#xff0c;幫助數據庫管理員執行文件系統操作和維護任務。這個擴展通常由數據庫超級用戶使用&#xff0c;提供了一些服務器端的文件訪問功能。 一、adminpack 擴展概述…

Unity | AmplifyShaderEditor插件基礎(第九集:旗子進階版)

目錄 一、&#x1f44b;&#x1f3fb;前言 二、準備工作 1.下載安裝插件ProBuilder 2.下載安裝插件Polybrush 3.固定原理 4.旗子 三、頂點上色 1.創建一個可以頂點上色的材質 2.開始上色 a.上色功能說明 b.全部上色 c.調整刷子 四、shader的設置 1.幅度添加 2.頂…

Java 實現 Excel 轉化為 PDF

引言 在實際開發中&#xff0c;將 Excel 文件轉化為 PDF 格式是一項常見需求。例如在需要共享數據報表時&#xff0c;PDF 格式具有更好的兼容性和安全性。GrapeCity Documents for Excel&#xff08;GcExcel&#xff09;為 Java 開發者提供了強大的工具&#xff0c;可輕松實現…

Spring Boot3批式訪問Dify聊天助手接口

Spring Boot3批式訪問Dify聊天助手接口 前言 之前已經配置好Dify1.4.1及LM Studio集成&#xff1a; https://lizhiyong.blog.csdn.net/article/details/148607462 現在就可以借助Spring Boot3去訪問Dify的后端接口&#xff0c;讓前端展示大模型的返回內容。這是我等大數據資…

事務傳播行為詳解

一、事務傳播行為的基本概念 事務傳播行為是Spring 框架中事務管理的核心概念&#xff0c;用于定義當一個事務方法被另一個事務方法調用時&#xff0c;事務應如何傳播。通俗地說&#xff0c;它解決了 “多個事務方法嵌套調用時&#xff0c;新方法是加入現有事務還是創建新事務…

Java八股文——Spring「SpringMVC 篇」

MVC分層介紹一下 面試官您好&#xff0c;MVC是一種非常經典、影響深遠的軟件設計模式&#xff0c;它的全稱是Model-View-Controller。在我看來&#xff0c;它的核心目標就是解決早期Web開發中&#xff0c;業務邏輯、數據和界面顯示高度耦合的問題&#xff0c;從而實現“各司其…

FreeSWITCH mod_curl 和 mod_xml_rpc 測試

編輯 /usr/local/freeswitch/conf/autoload_configs/xml_rpc.conf.xml <configuration name"xml_rpc.conf" description"XML RPC"> <settings> <param name"http-port" value"8889"/> <param name&quo…

實時監控、秒級決策:鏡舟科技如何重塑融資融券業務數據處理模式

融資融券業務作為證券市場的重要組成部分&#xff0c;已成為金融機構核心業務增長點和利潤來源。截至 2023 年底&#xff0c;我國融資融券余額已突破 1.8 萬億元&#xff0c;業務量呈現爆發式增長。然而&#xff0c;在業務高速發展的同時&#xff0c;金融機構面臨著數據處理效率…

Linux與量子計算:面向未來的架構演進

Linux與量子計算&#xff1a;面向未來的架構演進 當經典計算遇上量子革命 引言&#xff1a;量子計算時代的黎明 量子計算正從理論走向工程實踐&#xff0c;Linux作為現代計算的基石&#xff0c;正在量子革命中扮演關鍵角色。據IBM預測&#xff0c;到2027年&#xff0c;量子優勢…

Java中wait()為何必須同步調用?

在 Java 中&#xff0c;wait() 方法必須在 synchronized 方法或代碼塊中調用&#xff0c;主要原因如下&#xff1a; 1. 監視器鎖&#xff08;Monitor&#xff09;機制 依賴對象鎖&#xff1a;wait() 方法需要操作對象的監視器鎖&#xff08;Monitor&#xff09;&#xff0c;調…

前端面試專欄-基礎篇:4. 頁面渲染流程與性能優化

頁面渲染流程與性能優化詳解&#xff08;完整版&#xff09; 一、現代瀏覽器渲染流程&#xff08;詳細說明&#xff09; 1. 構建DOM樹 瀏覽器接收到HTML文檔后&#xff0c;會逐步解析并構建DOM&#xff08;Document Object Model&#xff09;樹。具體過程如下&#xff1a; (…

漲薪技術|Docker端口映射與容器互聯技術

前面的推文我們學了Docker操作的常用命令,今天開始給大家分享Docker端口映射與容器互聯,歡迎關注。Docker不管是程序員,架構師或者測試工程師都必須要掌握的一門主流技術。 Docker除了通過網絡訪問外,還提供了兩個很方便的功能來滿足服務訪問的基本需求,一個是允許映射容…

Roboguide工作站機器人重新安裝軟件包

1、點擊菜單欄“機器人-屬性”&#xff1b; 2、點擊“重新生成”&#xff1b; 3、點擊“確定”&#xff1b; 4、點擊“6&#xff1a;機器人選項” 5、在搜索框搜索軟件包&#xff0c;或在軟件包列表選擇&#xff0c;勾選軟件包后點擊“下一步”&#xff1b; 6、點擊“完成”&am…

預訓練CNN網絡的遷移學習(MATLAB例)

從基于大型數據集訓練的神經網絡中提取層&#xff0c;并基于新數據集進行微調。本例使用ImageNet中的子集進行微調。 This example retrains a SqueezeNet neural network using transfer learning. This network has been trained on over a million images, and can classif…

kali系統 windows Linux靶機入侵演練

Kali系統與Windows/Linux靶機入侵演練簡介 演練概述 Kali Linux是一款專為滲透測試和網絡安全評估設計的操作系統,常被安全專業人員用于合法的安全測試。入侵演練是網絡安全訓練的重要組成部分,旨在幫助安全人員了解攻擊手法并提升防御能力。 基本組件 1. **攻擊機**:通常…

手搓transformer

思路是這樣子的&#xff1a;從手搓代碼的角度去學習transformer&#xff0c;代碼會一個一個模塊地從頭到尾添加&#xff0c;以便學習者跟著敲&#xff0c;到最后再手搓一個基于tansformer的機器翻譯實戰項目。 transformer整體架構 一、輸入部分 詞向量 import torch import t…