Java 性能優化實戰(三):并發編程的 4 個優化維度

在多核CPU時代,并發編程是提升Java應用性能的關鍵手段,但不合理的并發設計反而會導致性能下降、死鎖等問題。本文將聚焦并發編程的四個核心優化方向,通過真實案例和代碼對比,帶你掌握既能提升性能又能保證線程安全的實戰技巧。

一、線程池參數調優:找到并發與資源的平衡點

線程池是并發編程的基礎組件,但參數設置不當會導致線程上下文切換頻繁、資源耗盡等問題。合理配置線程池參數能最大化利用CPU和IO資源。

線程池核心參數解析

線程池的核心參數決定了其工作特性:

  • 核心線程數(corePoolSize):保持運行的最小線程數
  • 最大線程數(maximumPoolSize):允許創建的最大線程數
  • 隊列容量(workQueue):用于保存待執行任務的阻塞隊列
  • 拒絕策略(handler):任務隊列滿時的處理策略

案例:線程池參數不合理導致的性能坍塌

某API網關系統使用線程池處理下游服務調用,壓測時發現TPS上不去,CPU使用率卻高達90%。

問題配置

// 錯誤配置:線程數過多,隊列無界
ExecutorService executor = new ThreadPoolExecutor(10,              // corePoolSize1000,            // maximumPoolSize(過大)60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>()  // 無界隊列
);

問題分析

  • 最大線程數設置為1000,遠超CPU核心數(16核),導致線程上下文切換頻繁
  • 無界隊列導致任務無限制堆積,內存占用持續增長
  • 線程過多導致CPU大部分時間用于切換線程,而非執行任務

優化配置

// 根據業務場景調整參數
int cpuCore = Runtime.getRuntime().availableProcessors();
ExecutorService executor = new ThreadPoolExecutor(cpuCore * 2,         // corePoolSize:IO密集型任務設為CPU核心數2倍cpuCore * 4,         // maximumPoolSize:控制在合理范圍60L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1000),  // 有界隊列,控制任務堆積new ThreadPoolExecutor.CallerRunsPolicy()  // 拒絕策略:讓調用者處理
);

優化效果

  • 線程數控制在64以內,上下文切換減少60%
  • CPU使用率從90%降至60%,但TPS提升了3倍
  • 內存使用趨于穩定,避免了OOM風險

線程池參數配置原則

  1. CPU密集型任務(如數據計算):

    • 核心線程數 = CPU核心數 + 1
    • 隊列使用ArrayBlockingQueue,容量適中
  2. IO密集型任務(如網絡請求、數據庫操作):

    • 核心線程數 = CPU核心數 * 2
    • 可適當增大最大線程數和隊列容量
  3. 隊列選擇

    • 優先使用有界隊列(如ArrayBlockingQueue),避免內存溢出
    • 任務優先級高時用PriorityBlockingQueue
  4. 拒絕策略

    • 核心服務用CallerRunsPolicy(犧牲部分性能保證任務不丟失)
    • 非核心服務用DiscardOldestPolicy或自定義策略

二、CompletableFuture:異步編程的性能利器

傳統的線程池+Future模式在處理多任務依賴時代碼繁瑣且效率低下,CompletableFuture提供了更靈活的異步編程模型,能顯著提升并發任務處理效率。

CompletableFuture核心優勢

  • 支持鏈式調用和任務組合
  • 提供豐富的異步回調方法
  • 可自定義線程池,避免使用公共線程池帶來的干擾

案例:訂單查詢接口的異步優化

某電商訂單詳情接口需要查詢訂單信息、用戶信息、商品信息和物流信息,傳統串行調用耗時過長。

串行實現(性能差)

// 串行調用,總耗時 = 各步驟耗時之和
public OrderDetail getOrderDetail(Long orderId) {Order order = orderService.getById(orderId);          // 50msUser user = userService.getById(order.getUserId());  // 40msList<Product> products = productService.listByIds(order.getProductIds());  // 60msLogistics logistics = logisticsService.getByOrderId(orderId);  // 70msreturn new OrderDetail(order, user, products, logistics);
}
// 總耗時:約50+40+60+70=220ms

CompletableFuture并行實現

// 自定義線程池,避免使用ForkJoinPool.commonPool()
private ExecutorService orderExecutor = new ThreadPoolExecutor(8, 16, 60L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(100),new ThreadFactory() {private final AtomicInteger counter = new AtomicInteger();@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName("order-detail-pool-" + counter.incrementAndGet());thread.setDaemon(true);return thread;}}
);public OrderDetail getOrderDetail(Long orderId) {try {// 1. 并行執行四個查詢CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(() -> orderService.getById(orderId), orderExecutor);CompletableFuture<User> userFuture = orderFuture.thenComposeAsync(order -> CompletableFuture.supplyAsync(() -> userService.getById(order.getUserId()), orderExecutor), orderExecutor);CompletableFuture<List<Product>> productFuture = orderFuture.thenComposeAsync(order -> CompletableFuture.supplyAsync(() -> productService.listByIds(order.getProductIds()), orderExecutor), orderExecutor);CompletableFuture<Logistics> logisticsFuture = CompletableFuture.supplyAsync(() -> logisticsService.getByOrderId(orderId), orderExecutor);// 2. 等待所有任務完成CompletableFuture.allOf(orderFuture, userFuture, productFuture, logisticsFuture).join();// 3. 組裝結果return new OrderDetail(orderFuture.get(),userFuture.get(),productFuture.get(),logisticsFuture.get());} catch (Exception e) {throw new ServiceException("查詢訂單詳情失敗", e);}
}
// 總耗時:約max(50,40,60,70)=70ms(并行執行)

優化效果

  • 接口響應時間從220ms降至70ms,性能提升68%
  • 系統吞吐量從500 TPS提升至1500 TPS
  • 資源占用更合理,避免了串行執行時的資源浪費

CompletableFuture實戰技巧

  1. 避免使用默認線程池:通過thenApplyAsyncsupplyAsync的第二個參數指定自定義線程池
  2. 異常處理:使用exceptionally()handle()方法處理異步任務異常
  3. 任務組合
    • thenCompose():串聯依賴任務
    • thenCombine():合并兩個獨立任務結果
    • allOf():等待所有任務完成
    • anyOf():等待任一任務完成

三、減少鎖粒度:從"大鎖"到"小鎖"的性能飛躍

鎖是保證線程安全的重要手段,但過大的鎖粒度會導致線程阻塞嚴重,通過減小鎖粒度能顯著提升并發性能。

鎖粒度優化思路

  • 將全局鎖拆分為多個局部鎖
  • 對數據分片加鎖,只鎖定操作的數據片段
  • 利用并發數據結構(如ConcurrentHashMap)替代手動加鎖

案例:庫存扣減的鎖粒度優化

某秒殺系統的庫存扣減操作使用全局鎖控制,導致并發搶購時大量線程阻塞。

全局鎖實現(性能瓶頸)

// 全局鎖導致所有商品的庫存操作都需要排隊
public class InventoryService {private final Object lock = new Object();private Map<Long, Integer> inventoryMap = new HashMap<>();  // 商品ID -> 庫存數量// 全局鎖:任何商品的扣減都需要獲取同一把鎖public boolean deduct(Long productId, int quantity) {synchronized (lock) {Integer stock = inventoryMap.get(productId);if (stock != null && stock >= quantity) {inventoryMap.put(productId, stock - quantity);return true;}return false;}}
}

分段鎖優化

public class InventoryService {// 1. 分16個段,降低鎖競爭private static final int SEGMENT_COUNT = 16;private final Segment[] segments = new Segment[SEGMENT_COUNT];private final Map<Long, Integer> inventoryMap = new ConcurrentHashMap<>();// 2. 每個段持有自己的鎖private static class Segment {final Object lock = new Object();}public InventoryService() {for (int i = 0; i < SEGMENT_COUNT; i++) {segments[i] = new Segment();}}// 3. 根據商品ID路由到不同的段,只鎖定對應段public boolean deduct(Long productId, int quantity) {// 計算路由到哪個段int segmentIndex = (int) (productId % SEGMENT_COUNT);Segment segment = segments[segmentIndex];// 只鎖定當前段,其他商品的操作不受影響synchronized (segment.lock) {Integer stock = inventoryMap.get(productId);if (stock != null && stock >= quantity) {inventoryMap.put(productId, stock - quantity);return true;}return false;}}
}

進一步優化:使用ConcurrentHashMap

public class InventoryService {// 利用ConcurrentHashMap的分段鎖機制private final ConcurrentHashMap<Long, Integer> inventoryMap = new ConcurrentHashMap<>();public boolean deduct(Long productId, int quantity) {// 循環重試機制處理并發更新while (true) {Integer currentStock = inventoryMap.get(productId);if (currentStock == null || currentStock < quantity) {return false;}// CAS機制更新庫存,避免顯式加鎖if (inventoryMap.replace(productId, currentStock, currentStock - quantity)) {return true;}// 更新失敗則重試}}
}

優化效果

  • 庫存扣減接口的并發能力從500 QPS提升至5000 QPS
  • 鎖等待時間從平均80ms降至5ms
  • 系統能穩定支撐秒殺場景的流量峰值

鎖優化的其他策略

  1. 鎖消除:JVM會自動消除不可能存在共享資源競爭的鎖
  2. 鎖粗化:將連續的細粒度鎖合并為一個粗粒度鎖,減少鎖開銷
  3. 讀寫分離鎖:使用ReentrantReadWriteLock,允許多個讀操作并發執行
  4. 無鎖編程:使用Atomic系列類、CAS操作替代鎖

四、volatile與ThreadLocal:輕量級并發工具的正確使用

volatile和ThreadLocal是Java提供的輕量級并發工具,合理使用能在保證線程安全的同時避免鎖帶來的性能開銷。

volatile:保證內存可見性的輕量級方案

volatile關鍵字能保證變量的內存可見性,但不能保證原子性,適用于狀態標記等場景。

正確使用場景

public class TaskRunner {// 用volatile保證stopFlag的可見性private volatile boolean stopFlag = false;public void start() {new Thread(() -> {while (!stopFlag) {  // 讀取volatile變量executeTask();}System.out.println("任務線程已停止");}).start();}// 其他線程調用此方法設置停止標記public void stop() {stopFlag = true;  // 寫入volatile變量}private void executeTask() {// 執行任務...}
}

常見誤區:試圖用volatile保證原子性

// 錯誤示例:volatile不能保證原子性
public class Counter {private volatile int count = 0;// 多線程調用時會出現計數錯誤public void increment() {count++;  // 非原子操作,包含讀-改-寫三個步驟}
}

ThreadLocal:線程私有變量的安全管理

ThreadLocal用于創建線程私有變量,避免多線程共享變量帶來的并發問題,特別適合上下文傳遞場景。

正確使用示例

public class UserContext {// 定義ThreadLocal存儲用戶上下文private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();// 設置當前線程的用戶上下文public static void setUser(User user) {userThreadLocal.set(user);}// 獲取當前線程的用戶上下文public static User getUser() {return userThreadLocal.get();}// 移除當前線程的用戶上下文,避免內存泄漏public static void removeUser() {userThreadLocal.remove();}
}// 使用場景:在攔截器中設置用戶上下文
public class AuthInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {User user = authenticate(request);  // 認證用戶UserContext.setUser(user);  // 設置到ThreadLocalreturn true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {UserContext.removeUser();  // 務必移除,避免內存泄漏}
}// 業務代碼中獲取用戶上下文
public class OrderService {public void createOrder() {User currentUser = UserContext.getUser();  // 無需參數傳遞,直接獲取// 創建訂單邏輯...}
}

ThreadLocal使用注意事項

  1. 必須移除:在任務結束或請求完成時調用remove(),避免線程池場景下的內存泄漏
  2. 避免存儲大對象:ThreadLocal中的對象會隨線程生命周期存在,大對象會占用過多內存
  3. 謹慎使用InheritableThreadLocal:它會傳遞給子線程,但可能導致意外的數據共享

并發編程優化的核心原則

并發編程優化的目標是在保證線程安全的前提下最大化系統吞吐量,核心原則包括:

  1. 最小化同步范圍:只對必要的代碼塊加鎖,減少線程阻塞時間
  2. 優先使用無鎖方案:Atomic系列、ConcurrentHashMap等并發工具性能優于顯式鎖
  3. 合理控制并發度:線程數并非越多越好,需根據CPU核心數和任務類型調整
  4. 避免線程饑餓:保證鎖的公平性或使用tryLock()避免長時間等待
  5. 完善監控告警:通過JMX或APM工具監控線程狀態、鎖競爭情況

記住:最好的并發設計是讓線程盡可能少地進行通信和同步,通過合理的任務拆分和數據隔離,實現"無鎖并發"的理想狀態。在實際開發中,需結合業務場景選擇合適的并發工具,通過壓測驗證優化效果,才能真正發揮并發編程的性能優勢。

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

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

相關文章

【秋招筆試】2025.08.19百度秋招機考第一套

?? 點擊直達筆試專欄 ??《大廠筆試突圍》 ?? 春秋招筆試突圍在線OJ ?? 筆試突圍在線刷題 bishipass.com 題目一:花園路徑優化問題 1??:使用棧維護必須保留的觀景點,基于三角不等式判斷 2??:貪心策略,檢查中間點是否為"轉折點" 3??:時間復雜度 …

SmartX 用戶建云實踐|某人壽保險:從開發測試、核心生產到信創轉型,按需推進企業云建設

某人壽保險自 2018 年起開始探索基于 SmartX 超融合架構搭建私有云 IaaS 資源池&#xff0c;先后部署了開發測試業務、生產業務和重要生產業務的 Oracle 數據庫&#xff08;含 RAC&#xff09;&#xff0c;并探索了基于海光芯片的信創云搭建&#xff0c;最終以基于超融合架構的…

通道注意力機制|Channel Attention Neural Network

一、通道注意力機制 論文&#xff1a;ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks 近年來&#xff0c;通道注意力機制在提高深度卷積神經網絡CNN的性能方面顯示出了巨大潛力。然而&#xff0c;大多數現有方法致力于開發更復雜的注意力模塊&a…

構建包含IK插件(中文分詞插件)的Elasticsearch鏡像

#!/bin/bash# 定義變量 ES_VERSION"8.15.3" IMAGE_NAME"elasticsearch-with-ik:${ES_VERSION}" IK_PLUGIN_DIR"./elasticsearch-analysis-ik-${ES_VERSION}" DOCKERFILE_NAME"Dockerfile.es-ik"# 檢查IK插件目錄是否存在 if [ ! -d &q…

Linux虛擬機安裝FTP

文章目錄深入理解FTP&#xff1a;從原理到實戰配置&#xff08;以VSFTP為例&#xff09;一、FTP基礎&#xff1a;你需要知道的核心概念1.1 什么是FTP&#xff1f;1.2 FTP的“雙端口”機制1.3 為什么選擇VSFTP&#xff1f;二、FTP的兩種工作模式&#xff1a;主動與被動2.1 主動模…

開源版CRM客戶關系管理系統源碼包+搭建部署教程

在數字化轉型的浪潮下&#xff0c;客戶關系管理&#xff08;CRM&#xff09;成為企業提升競爭力的關鍵工具。為滿足開發者和企業對個性化 CRM 系統的需求&#xff0c;分享一款開源版 CRM 客戶關系管理系統&#xff0c;其源碼涵蓋前臺、后臺及 Uniapp 源代碼&#xff0c;支持快速…

基于“R語言+遙感“水環境綜合評價方法技術應用——水線提取、水深提取、水溫提、水質提取、水環境遙感等

一&#xff1a;R語言1.1 R語言特點&#xff08;R語言&#xff09;1.2 安裝R&#xff08;R語言&#xff09;1.3 安裝RStudio&#xff08;R語言&#xff09;&#xff08;1&#xff09;下載地址&#xff08;2&#xff09;安裝步驟&#xff08;3&#xff09;軟件配置1.4 第一個程序…

MCP 與 Function Calling 打開真實世界的兩種“母體”方式

AI Agent的互動之言&#xff1a;當人工智能需要獲取實時信息或與外部環境進行交互時&#xff0c;它依賴于特定的技術機制來實現。本文將以通俗易懂的方式&#xff0c;深入解析MCP&#xff08;模型調用協議&#xff09;與函數調用的核心概念&#xff0c;比較二者的異同&#xff…

Ansys Motor-CAD:概述(EMag、THERM、LAB、MECH)

你好&#xff0c;在這篇博客中&#xff0c;我概述了如何使用 Ansys Motor-CAD 模型、模擬、分析和后處理結果來評估電機性能&#xff0c;并幫助您為您的應用選擇優化的電機&#xff0c;并通過電機設計選擇實現成本效益和效率。我介紹了各種可用的電機類型、可供選擇的物理模塊和…

AI + 金融領域 + 落地典型案例

目錄 一、美國銀行智能客服與風控體系 &#xff1a; 1. 推出了虛擬助手 Erica&#xff0c; 2. 構建了先進的風險評估模型&#xff0c; 二、財躍星辰與國泰海通、上海銀行合作項目&#xff1a; 1. 投教 AI 助手、投顧 AI 助手、托管 AI 助手 2. AI 手機銀行&#xff0c;對…

項目管理進階——研發項目組織管理制度

第一條 目的 為規范企業的新技術研發、技術創新工作,加強企業項目開發和技術創新能力,應用高新技術提高企業的整體市場競爭力和經濟效益,實施公司“科技興企”的重要決策,根據公司具體情況,特制定本辦法。 第二條 范圍 本辦法適用于以增強自主創新能力和促進企業高新技…

深度學習:入門簡介

深度學習&#xff08;Deep Learning, DL&#xff09;是機器學習&#xff08;Machine Learning, ML&#xff09;的一個重要分支&#xff0c;核心是通過模擬人類大腦神經元的連接方式&#xff0c;構建多層神經網絡來自動學習數據中的特征和規律&#xff0c;最終實現預測、分類、生…

switch搖桿JoyCon搖桿研究,碳膜搖桿、霍爾電磁搖桿

https://blog.csdn.net/qq_28145393/article/details/125769568 https://zhuanlan.zhihu.com/p/1925522678263056352 插件DIP 碳膜搖桿 6腳&#xff0c;內部兩個滑動變阻器&#xff0c;1個按鍵。 引腳定義如下&#xff1a;1腳AD1、2腳按鍵GND、3腳按鍵、4腳AD2、5腳變阻器GND、…

保護 PDF 格式:禁止轉換為其他格式文件

在日常辦公中&#xff0c;PDF是很常見的文件格式。有時候為了方便編輯&#xff0c;我們會將PDF轉換成其他格式文件&#xff0c;比如Word、PPT等&#xff1b;但有時候出于安全考慮&#xff0c;我們又不希望PDF可以隨意轉換成其他格式文件。那如何禁止轉換格式呢&#xff1f;其實…

docker 打包

目錄 構建docker容器 使用 Dockerfile 構建自定義鏡像 構建docker容器 docker images docker pull pytorch/torchserve:latest-gpu docker imagesdocker run -d --rm --gpus all --name torchserve-dev-bg -u $(id -u):$(id -g) -v /nas:/nas pytorch/torchserve:latest /bi…

云原生俱樂部-k8s知識點歸納(7)

計劃是再更兩篇就完結k8s系列&#xff0c;其中CRD客戶端資源定義會單獨列一篇&#xff0c;或許會講一講operator。不過當前的k8s并沒有細講operator&#xff0c;因為涉及到很多的go語言內容&#xff0c;以及相關的package的方法。這一部分主要就是講一講k8s如何進行監控和升級&…

c語言之進程函數

1. 進程創建#include <sys/types.h>#include <unistd.h>pid_t fork(void);fork 創建一個新進程fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is refe…

學習python第12天

今日任務&#xff1a;DataFrameDataFrame的構造pandas.DataFrame(dataNone, indexNone, columnsNone, dtypeNone, copyFalse)參數說明&#xff1a;data&#xff1a;DataFrame 的數據部分&#xff0c;可以是字典、二維數組、Series、DataFrame 或其他可轉換為 DataFrame 的對象。…

C++顯示類型轉換運算符static_cast使用指南

這是一篇關于 static_cast 用法的文章。本文會從基礎概念到常見應用場景全覆蓋&#xff0c;并附上代碼示例以方便理解。C 中的 static_cast 用法詳解 在 C 中&#xff0c;static_cast 是一種顯式類型轉換運算符&#xff0c;主要用于在編譯期進行類型安全的轉換。相比 C 風格的強…

es6常用方法來解決功能需求

前言&#xff1a;es6常用方法來解決功能需求。1、出現復雜的json字符串如何去解析&#xff1f;比如&#xff1a;下面這個字符串&#xff0c;如果用json.parse解析發現還是個字符串"\"[{\\\"orgId\\\":\\\"1054021138280960\\\",\\\"orgName…