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

面試與實戰(zhàn)必備:掌握Java并發(fā)編程關(guān)鍵知識點(線程池/CAS/性能優(yōu)化)

 更新時間:2026年05月23日 09:23:37   作者:百錦再@新空間創(chuàng)想科技  
本文從工程視角系統(tǒng)梳理Java并發(fā)編程的關(guān)鍵知識點,主要內(nèi)容包括:并發(fā)問題的本質(zhì)、Java內(nèi)存模型、volatile和synchronized、Lock與AQS、線程池、Future與CompletableFuture、CAS與原子類、并發(fā)容器、死鎖與線上排查、并發(fā)性能優(yōu)化思路等一些常用代碼片段


前言

Java 并發(fā)編程是后端開發(fā)中非常核心的一項能力。

很多線上問題表面上看起來是“偶發(fā)故障”,實際上根因都和并發(fā)有關(guān),比如:

  • 線程池被打滿
  • 鎖競爭嚴重
  • 某些數(shù)據(jù)偶爾不一致
  • 接口響應(yīng)時間抖動
  • CPU 飆高但吞吐沒有提升
  • 異步任務(wù)堆積
  • 死鎖
  • 可見性問題
  • 并發(fā)容器誤用

很多人學(xué)習并發(fā)時停留在 API 層,知道 synchronizedvolatile、ThreadPoolExecutor 的寫法,但遇到線上問題時,仍然很難定位和判斷。

這篇文章從工程視角出發(fā),系統(tǒng)梳理 Java 并發(fā)編程的關(guān)鍵知識點,幫助你把并發(fā)能力真正轉(zhuǎn)化成可落地的工程判斷力。

本文主要內(nèi)容包括:

  • 并發(fā)問題的本質(zhì)
  • Java 內(nèi)存模型
  • volatile 和 synchronized
  • Lock 與 AQS
  • 線程池設(shè)計
  • Future 與 CompletableFuture
  • CAS 與原子類
  • 并發(fā)容器
  • 死鎖與線上排查
  • 并發(fā)性能優(yōu)化思路

一、為什么并發(fā)問題總是在生產(chǎn)環(huán)境暴露

并發(fā)問題有一個典型特點:

本地不容易復(fù)現(xiàn),線上高峰期集中爆發(fā)。

原因很簡單。

并發(fā) bug 往往和時序有關(guān),而時序又受到下面這些因素影響:

  • 線程調(diào)度
  • CPU 競爭
  • GC 暫停
  • 網(wǎng)絡(luò)波動
  • 數(shù)據(jù)庫慢查詢
  • 下游服務(wù)阻塞
  • 機器負載

在本地開發(fā)環(huán)境下,請求量小、線程數(shù)少、資源競爭弱,很多問題根本暴露不出來。一旦進入高并發(fā)、長時間運行環(huán)境,隱藏問題就會被放大。

常見表現(xiàn)包括:

  • 計數(shù)不準
  • 數(shù)據(jù)重復(fù)處理
  • 某些請求偶發(fā)失敗
  • 響應(yīng)時間忽高忽低
  • 線程池任務(wù)堆積
  • 線程數(shù)量異常增長
  • CPU 占用高
  • 服務(wù)看起來沒掛,但幾乎無響應(yīng)

這類問題如果沒有并發(fā)基礎(chǔ),很難真正定位。

二、并發(fā)編程到底在解決什么

并發(fā)編程的本質(zhì)不是“讓代碼顯得高級”,而是為了在有限資源下提高吞吐、提升響應(yīng)效率,并確保結(jié)果正確。

它主要解決三類問題:

1. 性能問題

多個任務(wù)并行處理,提高資源利用率。

2. 正確性問題

多個線程同時讀寫共享數(shù)據(jù)時,如何保證結(jié)果不出錯。

3. 可控性問題

線程數(shù)量、任務(wù)隊列、鎖競爭、資源消耗必須有邊界。

很多開發(fā)者只盯著“并發(fā)更快”,但生產(chǎn)系統(tǒng)里更關(guān)鍵的是:

并發(fā)之后還能不能穩(wěn)定、可控、可排查。

三、進程、線程、協(xié)程先區(qū)分清楚

1. 進程

進程是資源分配的基本單位。一個 Java 應(yīng)用啟動后,對應(yīng)一個獨立進程。

2. 線程

線程是 CPU 調(diào)度的基本單位。一個進程內(nèi)部可以有多個線程,這些線程共享堆內(nèi)存、方法區(qū)等資源。

3. 協(xié)程

協(xié)程是更輕量級的執(zhí)行單元,通常在用戶態(tài)調(diào)度。Java 傳統(tǒng)并發(fā)主要以線程為核心,不過現(xiàn)在虛擬線程也在逐步發(fā)展。

對于大多數(shù) Java 后端開發(fā)者來說,目前最需要掌握的仍然是線程模型,因為:

  • 線程池
  • 并發(fā)容器
  • AQS
  • 可見性問題

這些核心知識都圍繞線程展開。

四、并發(fā) bug 的根源:共享、競爭、時序

并發(fā)代碼為什么難,根本原因就在于這三件事同時存在:

1. 共享

多個線程訪問同一份資源。

2. 競爭

多個線程同時修改共享資源。

3. 時序不可預(yù)測

線程何時運行、何時切換、誰先誰后,開發(fā)者很難完全控制。

只要代碼中出現(xiàn)“共享變量”,就要立刻問自己三個問題:

  • 會不會被多個線程訪問
  • 有沒有寫操作
  • 寫操作是否依賴順序

如果答案是“多個線程訪問,且至少一個線程會寫”,那就必須明確線程安全策略。

五、Java 內(nèi)存模型為什么必須理解

并發(fā)問題很多時候不是代碼邏輯錯,而是線程看到的變量值不一致。

Java 內(nèi)存模型,也就是 JMM,規(guī)定了:

  • 線程如何與主內(nèi)存交互
  • 線程何時能看到其他線程寫入的值
  • 哪些操作能保證有序性和可見性

理解 JMM,至少要掌握三個概念:

1. 原子性

一個操作是否不可分割。

2. 可見性

一個線程修改變量后,另一個線程能否立刻看到。

3. 有序性

程序執(zhí)行順序是否和代碼書寫順序一致。

舉個例子:

count++;

這不是原子操作,它至少包括三步:

  • 讀取 count
  • count + 1
  • 寫回 count

因此在多線程下會出現(xiàn)數(shù)據(jù)競爭。

六、volatile 的作用與邊界

volatile 的核心能力是:

  • 保證可見性
  • 一定程度上禁止指令重排序

示例:

private volatile boolean running = true;

一個線程將 running 改成 false,另一個線程通??梢院芸熳x到最新值。

volatile 適合什么場景

  • 狀態(tài)開關(guān)
  • 配置刷新標記
  • 一次寫、多次讀
  • 雙重檢查單例中的實例引用

volatile 不適合什么場景

  • 計數(shù)器自增
  • 余額扣減
  • 復(fù)合條件判斷
  • 依賴“讀-改-寫”原子性的邏輯

例如下面代碼依然線程不安全:

private volatile int count = 0;

public void increment() {
    count++;
}

因為 count++ 不是原子操作。

所以一定要記住一句話:

volatile 保證可見性,不保證復(fù)合操作原子性。

七、synchronized 的本質(zhì)

synchronized 是 Java 最基礎(chǔ)的內(nèi)置鎖。

它能保證:

  • 同一時刻只有一個線程進入臨界區(qū)
  • 進入和退出同步塊時具備可見性語義
  • 在同步范圍內(nèi)具備一定有序性保障

示例:

public synchronized void add() {
    count++;
}

或者:

synchronized (lock) {
    count++;
}

synchronized 適合什么場景

  • 簡單互斥訪問
  • 臨界區(qū)較小
  • 對鎖控制要求不復(fù)雜
  • 代碼可讀性優(yōu)先

很多開發(fā)者印象里覺得 synchronized 性能很差,這個認知已經(jīng)過時。JDK 對它做過大量優(yōu)化,很多場景下完全夠用。

八、ReentrantLock 為什么存在

如果你對鎖有更細粒度的控制需求,ReentrantLock 就比 synchronized 更適合。

示例:

private final ReentrantLock lock = new ReentrantLock();

public void add() {
    lock.lock();
    try {
        count++;
    } finally {
        lock.unlock();
    }
}

ReentrantLock 的優(yōu)勢

  • 支持可中斷獲取鎖
  • 支持 tryLock()
  • 支持超時獲取鎖
  • 支持公平鎖
  • 支持多個 Condition

什么時候用 ReentrantLock

  • 需要超時等待鎖
  • 需要中斷等待鎖
  • 需要多個條件隊列
  • 需要更細致的鎖控制

但同時要注意:

顯式鎖一定要在 finally 中釋放。

九、AQS 到底是什么

AQS,全稱 AbstractQueuedSynchronizer,是 Java 并發(fā)包里非常核心的同步框架。

很多常見同步工具都建立在 AQS 之上,例如:

  • ReentrantLock
  • Semaphore
  • CountDownLatch
  • ReentrantReadWriteLock
  • FutureTask

AQS 的核心思路可以概括為:

  • 用一個 state 表示同步狀態(tài)
  • 用等待隊列保存競爭失敗的線程
  • 線程獲取資源失敗后排隊等待
  • 資源釋放后喚醒后繼節(jié)點

理解 AQS 的意義不在于每天自己手寫同步器,而在于你能真正理解:

  • 鎖是怎么排隊的
  • 線程為什么阻塞
  • 為什么有鎖競爭
  • 為什么線程會被喚醒

十、synchronized 和 ReentrantLock 怎么選

簡單來說可以這樣判斷:

優(yōu)先用 synchronized 的場景

  • 只需要簡單互斥
  • 臨界區(qū)邏輯不復(fù)雜
  • 不需要嘗試獲取鎖
  • 不需要超時控制
  • 更看重代碼簡潔

優(yōu)先用 ReentrantLock 的場景

  • 需要可中斷
  • 需要 tryLock
  • 需要公平鎖
  • 需要多個條件變量
  • 需要更細粒度鎖控制

不要為了“顯得專業(yè)”把所有同步都替換成 ReentrantLock。大多數(shù)場景,簡單的方案更穩(wěn)。

十一、讀寫鎖什么時候適合使用

如果一個共享資源是典型的“讀多寫少”,讀寫鎖通常能帶來更好的并發(fā)能力。

示例:

private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();

讀操作:

readLock.lock();
try {
    return cache.get(key);
} finally {
    readLock.unlock();
}

寫操作:

writeLock.lock();
try {
    cache.put(key, value);
} finally {
    writeLock.unlock();
}

適合讀寫鎖的場景

  • 緩存
  • 配置讀取
  • 字典數(shù)據(jù)
  • 查詢遠多于更新的共享資源

不適合的場景

  • 寫操作頻繁
  • 臨界區(qū)很小
  • 鎖維護成本高于收益

十二、線程池為什么是并發(fā)系統(tǒng)的核心

生產(chǎn)環(huán)境里,幾乎不應(yīng)該頻繁手動 new Thread()。

原因很簡單:

  • 線程創(chuàng)建和銷毀有成本
  • 線程數(shù)不受控會壓垮系統(tǒng)
  • 線程切換也有成本
  • 缺乏統(tǒng)一管理和監(jiān)控

線程池的核心價值就是:

統(tǒng)一管理線程資源,并建立明確邊界。

線程池的關(guān)鍵參數(shù)包括:

  • corePoolSize
  • maximumPoolSize
  • keepAliveTime
  • workQueue
  • threadFactory
  • RejectedExecutionHandler

示例:

ExecutorService executor = new ThreadPoolExecutor(
        8,
        16,
        60L,
        TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(1000),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.CallerRunsPolicy()
);

十三、為什么生產(chǎn)環(huán)境不建議直接用 Executors

很多人習慣直接這樣寫:

Executors.newFixedThreadPool(10);

或者:

Executors.newCachedThreadPool();

這類 API 雖然方便,但在生產(chǎn)環(huán)境通常不推薦直接使用,原因是關(guān)鍵參數(shù)被隱藏了。

典型風險

1. newFixedThreadPool

默認使用無界隊列,任務(wù)堆積時可能不斷占用內(nèi)存。

2. newCachedThreadPool

線程數(shù)增長過快時可能導(dǎo)致大量線程創(chuàng)建,造成調(diào)度和資源壓力。

生產(chǎn)環(huán)境更合理的方式是:

顯式使用 ThreadPoolExecutor,把線程數(shù)、隊列長度、拒絕策略寫清楚。

十四、線程池參數(shù)如何估算

線程池沒有萬能配置,但可以從任務(wù)類型入手。

1. CPU 密集型任務(wù)

例如:

  • 加密解密
  • 規(guī)則計算
  • 圖像處理
  • 復(fù)雜算法

這類任務(wù)線程數(shù)通常接近 CPU 核數(shù)。

經(jīng)驗值:

CPU核數(shù) + 1

2. IO 密集型任務(wù)

例如:

  • 數(shù)據(jù)庫訪問
  • Redis 調(diào)用
  • HTTP 調(diào)用
  • 文件讀寫

這類任務(wù)線程經(jīng)常在等待,可以適當提高線程數(shù)。

3. 真正的調(diào)優(yōu)原則

線程池參數(shù)不能只靠經(jīng)驗公式,最終一定要看:

  • 接口響應(yīng)時間
  • 隊列堆積情況
  • 拒絕次數(shù)
  • CPU 使用率
  • 下游資源瓶頸

十五、拒絕策略為什么不能忽略

線程池不是無限吞任務(wù)的。

當線程數(shù)達到上限、隊列也滿了之后,線程池會觸發(fā)拒絕策略。

JDK 常見拒絕策略有四種:

  • AbortPolicy
  • CallerRunsPolicy
  • DiscardPolicy
  • DiscardOldestPolicy

各自特點

1. AbortPolicy

直接拋異常。適合需要明確感知失敗的場景。

2. CallerRunsPolicy

由提交任務(wù)的線程自己執(zhí)行,適合做一定程度的反壓。

3. DiscardPolicy

直接丟棄任務(wù),不拋異常。高風險,不適合關(guān)鍵任務(wù)。

4. DiscardOldestPolicy

丟棄最早排隊的任務(wù)。是否合理取決于業(yè)務(wù)語義。

最危險的不是哪種策略不好,而是:

系統(tǒng)已經(jīng)在拒絕任務(wù)了,但沒人知道。

所以拒絕次數(shù)一定要納入監(jiān)控。

十六、Future、Callable、CompletableFuture 的使用場景

1. Callable 和 Future

Callable 相比 Runnable,支持返回值和異常。

示例:

Future<Integer> future = executor.submit(() -> 1 + 2);
Integer result = future.get();

問題在于:

  • Future 組合能力差
  • 多任務(wù)編排麻煩
  • 異常傳播不優(yōu)雅

2. CompletableFuture

如果需要做異步編排,CompletableFuture 更適合。

例如:

CompletableFuture<User> userFuture =
        CompletableFuture.supplyAsync(() -> userService.getUser(userId), executor);

CompletableFuture<List<Order>> orderFuture =
        CompletableFuture.supplyAsync(() -> orderService.getOrders(userId), executor);

CompletableFuture<UserDetailDTO> resultFuture =
        userFuture.thenCombine(orderFuture, UserDetailDTO::new);

使用 CompletableFuture 的注意點

  • 生產(chǎn)環(huán)境盡量顯式指定線程池
  • 不要把所有業(yè)務(wù)都異步化
  • 要明確異常處理鏈路
  • 不要忽略下游資源瓶頸

十七、CAS 和原子類的意義

CAS 是 Compare-And-Swap,通過比較當前值與期望值是否一致來決定是否更新。

Java 中常見原子類包括:

  • AtomicInteger
  • AtomicLong
  • AtomicReference
  • LongAdder

示例:

AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();

CAS 的優(yōu)點

  • 輕量
  • 低沖突下性能好
  • 無需傳統(tǒng)互斥鎖

CAS 的限制

  • 高沖突時會反復(fù)重試
  • 會消耗 CPU
  • 不適合復(fù)雜臨界區(qū)邏輯
  • 存在 ABA 問題

所以原子類適合做:

  • 計數(shù)器
  • 狀態(tài)位切換
  • 簡單引用更新

不適合直接替代所有加鎖場景。

十八、AtomicLong 和 LongAdder 怎么選

AtomicLong 適合

  • 并發(fā)不高
  • 單點計數(shù)
  • 需要簡單直接

LongAdder 適合

  • 高并發(fā)熱點計數(shù)
  • 指標統(tǒng)計
  • 訪問次數(shù)累加
  • 監(jiān)控場景

原因是 LongAdder 會分散競爭熱點,最后再匯總結(jié)果,在高并發(fā)統(tǒng)計場景下通常更有優(yōu)勢。

十九、并發(fā)容器如何選型

常見并發(fā)容器包括:

  • ConcurrentHashMap
  • CopyOnWriteArrayList
  • ConcurrentLinkedQueue
  • BlockingQueue

1. ConcurrentHashMap

適合緩存、本地索引、狀態(tài)映射。

ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();

2. CopyOnWriteArrayList

適合讀多寫少場景,比如監(jiān)聽器列表。

3. BlockingQueue

適合生產(chǎn)者消費者模型、異步任務(wù)緩沖。

4. ConcurrentLinkedQueue

適合高并發(fā)無阻塞隊列場景。

這里一定要記住一句話:

并發(fā)容器保證的是單次容器操作線程安全,不保證多個組合操作天然線程安全。

例如下面寫法就不是絕對安全的:

if (!map.containsKey(key)) {
    map.put(key, value);
}

應(yīng)優(yōu)先使用:

map.putIfAbsent(key, value);

或者:

map.computeIfAbsent(key, k -> createValue(k));

二十、阻塞隊列的重要性

阻塞隊列在并發(fā)系統(tǒng)里非常關(guān)鍵,尤其在線程池和生產(chǎn)者消費者模型中。

常見阻塞隊列有:

  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • SynchronousQueue
  • DelayQueue
  • PriorityBlockingQueue

使用阻塞隊列時要考慮什么

  • 是否需要有界
  • 是否允許任務(wù)堆積
  • 是否需要優(yōu)先級
  • 是否是延遲任務(wù)
  • 是否需要直接移交

這里最重要的一個原則是:

高并發(fā)系統(tǒng)優(yōu)先考慮有界隊列。

無界隊列會把問題“延后爆炸”,而不是解決問題。

二十一、死鎖為什么難排查

死鎖通常發(fā)生在多個線程相互等待對方釋放資源時。

典型場景:

  • 線程 A 持有鎖 A,等待鎖 B
  • 線程 B 持有鎖 B,等待鎖 A

示例:

synchronized (lockA) {
    synchronized (lockB) {
    }
}

另一段代碼反過來:

synchronized (lockB) {
    synchronized (lockA) {
    }
}

預(yù)防死鎖的原則

  • 統(tǒng)一加鎖順序
  • 減少鎖嵌套
  • 縮小鎖粒度
  • 避免持鎖期間做遠程調(diào)用
  • 必要時使用 tryLock + timeout

線上排查死鎖最常用的手段是:

jstack

二十二、ThreadLocal 為什么常用又危險

ThreadLocal 用來為每個線程保存獨立變量副本。

適合場景:

  • 當前登錄用戶上下文
  • traceId
  • 請求級上下文
  • 格式化對象緩存

示例:

private static final ThreadLocal<String> CURRENT_USER = new ThreadLocal<>();

風險點

如果在線程池環(huán)境中使用 ThreadLocal,但沒有及時清理,線程復(fù)用后就可能讀到舊值,甚至導(dǎo)致內(nèi)存泄漏。

正確寫法:

try {
    CURRENT_USER.set(userId);
    // 業(yè)務(wù)邏輯
} finally {
    CURRENT_USER.remove();
}

結(jié)論很明確:

線程池環(huán)境中使用 ThreadLocal,一定要 remove。

二十三、并發(fā)性能優(yōu)化中的常見誤區(qū)

誤區(qū) 1:線程越多越快

錯誤。線程太多會帶來上下文切換和資源競爭。

誤區(qū) 2:鎖一定很慢

錯誤。低競爭場景下鎖可能非常高效。

誤區(qū) 3:異步一定比同步高級

錯誤。異步只是改變執(zhí)行方式,不一定更優(yōu)。

誤區(qū) 4:CPU 高說明系統(tǒng)在努力工作

錯誤。高 CPU 可能是自旋、死循環(huán)、過度序列化、GC 或競爭。

誤區(qū) 5:用了線程池就萬事大吉

錯誤。線程池參數(shù)配置、隊列長度、拒絕策略、任務(wù)拆分方式都會決定最終效果。

并發(fā)優(yōu)化必須建立在真實監(jiān)控和壓測數(shù)據(jù)之上,而不是憑感覺。

二十四、線上并發(fā)問題如何排查

排查并發(fā)問題時,建議按下面順序看。

1. 先看癥狀

  • 是響應(yīng)慢
  • 還是錯誤率高
  • 還是吞吐下降
  • 還是 CPU 飆高
  • 還是線程數(shù)過多

2. 看線程池

  • 活動線程數(shù)
  • 隊列長度
  • 拒絕次數(shù)
  • 最大線程數(shù)是否打滿

3. 看線程棧

使用 jstack 查看:

  • 是否有大量 BLOCKED 線程
  • 是否有 WAITING 線程堆積
  • 是否有死鎖
  • 是否有長時間卡在 IO

4. 看下游資源

  • 數(shù)據(jù)庫連接池是否耗盡
  • Redis 是否超時
  • HTTP 下游是否變慢

5. 看 GC

長時間 STW 也會放大并發(fā)問題表象。

6. 看業(yè)務(wù)設(shè)計

  • 是否線程池共用導(dǎo)致互相影響
  • 是否任務(wù)拆分太碎
  • 是否熱點 key 導(dǎo)致競爭
  • 是否異步任務(wù)沒有邊界

二十五、一個典型線程池事故案例

假設(shè)一個訂單系統(tǒng),一個請求會觸發(fā)這些動作:

  • 查用戶
  • 查庫存
  • 創(chuàng)建訂單
  • 發(fā)短信
  • 發(fā)通知
  • 寫日志
  • 更新推薦系統(tǒng)

開發(fā)者為了“提升性能”,把后面所有動作都異步化,丟進同一個線程池。

線程池參數(shù)如下:

  • 核心線程 20
  • 最大線程 200
  • 無界隊列

低峰期沒問題,高峰期庫存服務(wù)一慢,異步任務(wù)消費速度下降。因為隊列無界,任務(wù)會不斷積壓。短時間內(nèi)看不出異常,但隨著任務(wù)越堆越多,內(nèi)存、GC、延遲都會變差,最后系統(tǒng)整體雪崩。

這個案例說明幾個關(guān)鍵問題:

  • 線程池不能混用所有業(yè)務(wù)
  • 無界隊列非常危險
  • 下游慢必須有超時和降級
  • 線程池必須監(jiān)控
  • 異步并不等于高性能

二十六、并發(fā)代碼設(shè)計的硬規(guī)則

下面這些原則非常實用。

1. 優(yōu)先減少共享狀態(tài)

不共享,就沒有競爭。

2. 顯式創(chuàng)建線程池

不要依賴默認線程池或偷懶工廠方法。

3. 所有共享變量都要明確線程安全策略

要么鎖、要么原子類、要么線程封閉、要么不可變。

4. 組合操作要特別謹慎

容器線程安全不代表業(yè)務(wù)邏輯整體線程安全。

5. 鎖粒度盡量小

持鎖期間不要做慢操作、遠程調(diào)用、數(shù)據(jù)庫操作。

6. 高并發(fā)熱點計數(shù)優(yōu)先用 LongAdder

不要讓所有線程爭搶同一個熱點原子變量。

7. 線程池、鎖、隊列必須有監(jiān)控

不可觀測的并發(fā)系統(tǒng)基本不可維護。

8. 不要為了并發(fā)而并發(fā)

如果真正瓶頸在數(shù)據(jù)庫或網(wǎng)絡(luò),多開線程不一定有效。

二十七、如何高效學(xué)習 Java 并發(fā)

學(xué)習并發(fā)最有效的方式不是死記硬背,而是結(jié)合代碼和問題去理解。

建議從三個方向入手。

1. 自己寫小實驗

例如:

  • 多線程計數(shù)器
  • volatile 可見性測試
  • synchronized 和 AtomicInteger 對比
  • 線程池參數(shù)實驗

2. 結(jié)合源碼學(xué)習

重點建議看:

  • ThreadPoolExecutor
  • ConcurrentHashMap
  • ReentrantLock
  • CountDownLatch
  • CompletableFuture

3. 帶著線上問題學(xué)習

比如:

  • 為什么線程池會堆積
  • 為什么接口偶發(fā)超時
  • 為什么 CPU 高但吞吐沒上去
  • 為什么會死鎖

有問題驅(qū)動,理解會更扎實。

總結(jié)

Java 并發(fā)編程不是幾個 API 的使用技巧,而是一整套關(guān)于共享資源、執(zhí)行時序、資源邊界和系統(tǒng)穩(wěn)定性的工程能力。

如果把全文壓縮成幾條最重要的結(jié)論,就是下面這些:

  • 線程安全的核心不是加鎖,而是控制共享和競爭
  • volatile 解決可見性,不解決復(fù)合操作原子性
  • 簡單互斥優(yōu)先 synchronized,復(fù)雜控制再考慮 Lock
  • 線程池必須顯式配置,參數(shù)和隊列一定要可控
  • 并發(fā)容器保證單次操作安全,不保證復(fù)合邏輯天然安全
  • 并發(fā)優(yōu)化必須基于監(jiān)控和壓測,不要憑感覺調(diào)整
  • 線上并發(fā)問題本質(zhì)上往往是資源模型和邊界設(shè)計問題

真正掌握并發(fā),不是會背定義,而是你開始能在寫業(yè)務(wù)代碼時主動識別:

  • 哪些狀態(tài)會共享
  • 哪些地方會競爭
  • 哪些操作需要隔離
  • 哪些任務(wù)需要限流
  • 哪些線程池必須拆分
  • 哪些鎖會成為瓶頸

當你具備這種判斷力時,并發(fā)才真正成為你的工程能力,而不是面試題。

常用代碼片段匯總

synchronized

public synchronized void add() {
    count++;
}

ReentrantLock

lock.lock();
try {
    count++;
} finally {
    lock.unlock();
}

AtomicInteger

AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();

ThreadPoolExecutor

ExecutorService executor = new ThreadPoolExecutor(
        8, 16, 60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(1000),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.CallerRunsPolicy()
);

CompletableFuture

CompletableFuture<User> userFuture =
        CompletableFuture.supplyAsync(() -> userService.getUser(userId), executor);

到此這篇關(guān)于面試與實戰(zhàn)必備:掌握Java并發(fā)編程關(guān)鍵知識點(線程池/CAS/性能優(yōu)化)的文章就介紹到這了,更多相關(guān)Java并發(fā)編程核心知識點梳理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實現(xiàn)添加、驗證PDF數(shù)字簽名的方法示例

    Java實現(xiàn)添加、驗證PDF數(shù)字簽名的方法示例

    在設(shè)置文檔內(nèi)容保護的方法中,除了對文檔加密、添加水印外,應(yīng)用數(shù)字簽名也是一種有效防偽手段。本文就使用Java實現(xiàn)添加、驗證PDF數(shù)字簽名,感興趣的可以了解一下
    2021-07-07
  • Java填充替換數(shù)組元素實例詳解

    Java填充替換數(shù)組元素實例詳解

    這篇文章主要通過兩個實例說明Java填充和替換數(shù)組中元素的方法,需要的朋友可以參考下。
    2017-08-08
  • Java中的runnable 和 callable 區(qū)別解析

    Java中的runnable 和 callable 區(qū)別解析

    Runnable接口用于定義不需要返回結(jié)果的任務(wù),而Callable接口可以返回結(jié)果并拋出異常,通常與Future結(jié)合使用,Runnable適用于簡單的后臺任務(wù)和定時任務(wù),而Callable適用于并行計算、異步操作和復(fù)雜任務(wù),選擇使用哪個接口取決于具體的應(yīng)用場景,感興趣的朋友一起看看吧
    2025-03-03
  • java中xml進行報文發(fā)送和解析操作

    java中xml進行報文發(fā)送和解析操作

    這篇文章主要介紹了java中xml進行報文發(fā)送和解析操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • jdk15的安裝與配置全過程記錄

    jdk15的安裝與配置全過程記錄

    這篇文章主要給大家介紹了關(guān)于jdk15的安裝與配置,文中通過圖文介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2021-01-01
  • Java得到一個整數(shù)的絕對值,不使用任何判斷和比較語句,包括API

    Java得到一個整數(shù)的絕對值,不使用任何判斷和比較語句,包括API

    Java得到一個整數(shù)的絕對值,不使用任何判斷和比較語句,包括API
    2009-09-09
  • SpringAOP 設(shè)置注入的實現(xiàn)步驟

    SpringAOP 設(shè)置注入的實現(xiàn)步驟

    這篇文章主要介紹了SpringAOP 設(shè)置注入的實現(xiàn)步驟,幫助大家更好的理解和學(xué)習使用Spring框架,感興趣的朋友可以了解下
    2021-05-05
  • SpringBoot中的自定義starter

    SpringBoot中的自定義starter

    這篇文章主要介紹了SpringBoot中的自定義starter,Starter是Spring?Boot中的一個非常重要的概念,Starter相當于模塊,它能將模塊所需的依賴整合起來并對模塊內(nèi)的Bean根據(jù)環(huán)境(條件)進行自動配置,需要的朋友可以參考下
    2024-01-01
  • SpringBoot 集成 Memcached的方法示例

    SpringBoot 集成 Memcached的方法示例

    這篇文章主要介紹了SpringBoot 集成 Memcached的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2019-05-05
  • JDBC數(shù)據(jù)源連接池配置及應(yīng)用

    JDBC數(shù)據(jù)源連接池配置及應(yīng)用

    這篇文章主要介紹JDBC建立數(shù)據(jù)庫連接的兩種方式,使用配置數(shù)據(jù)源的方式連接數(shù)據(jù)庫,效率更高,推薦使用,希望能給大家做一個參考。
    2016-06-06

最新評論

永泰县| 垫江县| 南澳县| 股票| 凌海市| 吉林市| 堆龙德庆县| 丹巴县| 大埔县| 彭山县| 永吉县| 汶川县| 库伦旗| 康马县| 宜宾市| 隆化县| 樟树市| 安顺市| 屏边| 林州市| 蚌埠市| 宁陕县| 安陆市| 兰西县| 永善县| 太原市| 宁化县| 科技| 郧西县| 靖边县| 诸城市| 茂名市| 溧水县| 高唐县| 普洱| 根河市| 新昌县| 利辛县| 台州市| 调兵山市| 茶陵县|