Java 多線程進階:線程安全、synchronized、死鎖、wait/notify 全解析(含代碼示例)

在 Java 并發編程中,“線程安全” 是核心議題之一。本文將深入講解線程安全的實現手段、synchronized 的使用方式、可重入鎖、死鎖的成因與避免、wait/notify 通信機制等,并配合實際代碼案例,幫助你徹底搞懂 Java 線程協作機制。


一、線程安全與加鎖機制

1. synchronized 的使用方式

synchronized 是 Java 最基本的加鎖工具,保證代碼塊在多個線程中“互斥”執行。

① 修飾普通方法(鎖的是當前實例 this
public synchronized void syncMethod() {// 線程安全的邏輯
}

② 修飾靜態方法(鎖的是當前類的 .class 對象)

public synchronized static void staticSyncMethod() {// 靜態同步邏輯
}

③ 修飾代碼塊(可以靈活選擇鎖對象)

public void method() {synchronized (this) {// 同步邏輯}
}

2. 鎖競爭與鎖沖突

  • 同一對象加鎖:多個線程競爭同一把鎖,會造成阻塞等待(鎖沖突)。

  • 不同對象加鎖:互不干擾,線程可并發執行。

Runnable task = () -> {synchronized (lockObject) {// 臨界區代碼}
};

二、可重入性:不會死鎖的“重復加鎖”

Java 的 synchronized可重入鎖。也就是說,一個線程可以多次獲得同一把鎖,不會導致死鎖。

public synchronized void outer() {inner(); // 同一線程再次進入 synchronized 方法
}public synchronized void inner() {// 安全執行
}

三、死鎖問題與避免

死鎖產生的典型場景

1. 兩個線程兩把鎖,互相等待對方釋放
class DeadlockExample {private final Object lock1 = new Object();private final Object lock2 = new Object();public void task1() {synchronized (lock1) {System.out.println("Task1 獲得了lock1");try { Thread.sleep(100); } catch (InterruptedException ignored) {}synchronized (lock2) {System.out.println("Task1 獲得了lock2");}}}public void task2() {synchronized (lock2) {System.out.println("Task2 獲得了lock2");try { Thread.sleep(100); } catch (InterruptedException ignored) {}synchronized (lock1) {System.out.println("Task2 獲得了lock1");}}}
}

?這個例子滿足死鎖的 4 個必要條件,其中最核心的是“循環等待”。

死鎖避免策略

  • 統一加鎖順序:總是先加 lock1,再加 lock2,避免循環依賴。

  • 使用 tryLock() + 超時機制(需使用 ReentrantLock)。


四、線程通信:wait 和 notify 的正確使用方式

使用wait()notify() 方法

  • wait():線程 自愿等待,進入“暫停”狀態,直到被別人叫醒。

  • notify()叫醒一個正在等待的線程。

  • notifyAll():叫醒所有等待的線程(但只有一個能拿到鎖繼續執行)。

使用場景舉例:先執行線程 t1 的一部分,再由線程 t2 接力。

class Task {private final Object lock = new Object();private boolean ready = false;public void part1() {synchronized (lock) {System.out.println("T1 正在執行前半部分任務");try { Thread.sleep(1000); } catch (InterruptedException ignored) {}ready = true;lock.notify(); // 喚醒 T2}}public void part2() {synchronized (lock) {while (!ready) {try {lock.wait(); // 主動釋放鎖并阻塞} catch (InterruptedException ignored) {}}System.out.println("T2 收到通知,繼續執行后續任務");}}
}public class WaitNotifyDemo {public static void main(String[] args) {Task task = new Task();Thread t1 = new Thread(task::part1);Thread t2 = new Thread(task::part2);t2.start(); // T2 先 waitt1.start(); // T1 后 notify}
}

join()sleep() 相比,wait/notify 更靈活,支持提前喚醒和條件控制。


wait 的底層流程

  1. 釋放鎖

  2. 阻塞等待

  3. 被喚醒后重新競爭鎖

  4. 重新獲取鎖并繼續執行


notify 與 notifyAll 的區別

  • notify():隨機喚醒一個正在 wait() 的線程。

  • notifyAll():喚醒所有等待線程,但只有一個能成功獲得鎖。


五、volatile 與內存可見性

在多線程環境中,每個線程可能并不直接操作主內存中的變量,而是從主內存讀取變量到自己的緩存中進行操作。這就可能出現這樣的情況:

  • 一個線程修改了變量的值,但另一個線程看不到這個變化(因為仍在用舊的緩存)。

  • 導致線程間的通信出現“看不見的修改”。

這就是內存可見性問題

示例代碼

public class VisibilityProblem {private static boolean running = true;public static void main(String[] args) {Thread t = new Thread(() -> {while (running) {// 執行代碼}System.out.println("線程停止");});t.start();try { Thread.sleep(1000); } catch (InterruptedException ignored) {}running = false; // 主線程修改 runningSystem.out.println("主線程修改 running 為 false");}
}

可能結果:

即使主線程已經把 running 改為 falset 線程可能還一直在死循環,因為它使用的是本地緩存值而不是主內存的值。

解決方式:使用 volatile

private static volatile boolean running = true;

?一旦使用 volatile 修飾變量,修改后的值會立刻刷新到主內存,并且所有線程每次訪問變量時都會從主內存讀取,從而保證了內存可見性。


結語

Java 多線程的本質是對“共享資源 + 并發訪問”下的一種控制與協作。理解 synchronized 的使用方式、死鎖的本質、以及 wait/notify 的協作機制,能有效幫助我們寫出更安全、靈活的并發程序。

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

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

相關文章

高并發場景下的MySQL生存指南

引言 在2025年全球數字經濟峰會上,阿里云披露其核心交易系統單日處理請求量突破萬億次,其中MySQL集群承載了78%的OLTP業務。這標志著數據庫系統已進入百萬級QPS時代,傳統優化手段面臨三大挑戰: 一、硬件與架構優化:構…

MCP入門

什么是mcp mcp(model context protocol,模型上下文協議) 標準化協議:讓大模型用統一的方式來調用工具,是llm和工具之間的橋梁 A2A:Agent-to-Agent協議 mcp通信機制 提供mcp服務查詢的平臺 具有工具合集…

服務容錯治理框架resilience4jsentinel基礎應用---微服務的限流/熔斷/降級解決方案

繼續上一章未完成的sentinel; 直接實操; 關于測試:本文使用線程池線程異步執行模擬并發結合Mock框架測試 其他文章 服務容錯治理框架resilience4j&sentinel基礎應用---微服務的限流/熔斷/降級解決方案-CSDN博客 conda管理python環境-…

深入理解 C 語言中的變量作用域與鏈接性:`extern`、`static` 與全局變量

深入理解 C 語言中的變量作用域與鏈接性:extern、static 與全局變量 在 C 語言中,變量的作用域(Scope)和鏈接性(Linkage)是理解程序結構和模塊化的關鍵概念。本文將詳細探討在函數外定義的變量是否為全局變…

實驗三 軟件黑盒測試

實驗三 軟件黑盒測試使用測試界的一個古老例子---三角形問題來進行等價類劃分。輸入三個整數a、b和c分別作為三角形的三條邊,通過程序判斷由這三條邊構成的三角形類型是等邊三角形、等腰三角形、一般三角形或非三角形(不能構成一個三角形)。其中要求輸入變量&#x…

小米首個推理大模型開源——Xiaomi MiMo,為推理而戰!

名人說:路漫漫其修遠兮,吾將上下而求索。—— 屈原《離騷》 創作者:Code_流蘇(CSDN)(一個喜歡古詩詞和編程的Coder😊) 目錄 一、MiMo的驚人表現:小參數量,大能力二、雙輪驅動&#…

《2025全球機器學習技術大會:阿里云講師張玉明深度剖析通義靈碼AI程序員》

4 月 18 日 - 19 日,由 CSDN & Boolan 聯合舉辦的 2025 全球機器學習技術大會(ML-Summit)于上海順利舉行。大會聚焦人工智能與機器學習前沿技術,匯聚了來自科技與人工智能領域的數位頂尖專家以及數千名開發者和研究者&#xf…

MySQL事務隔離級別詳解

MySQL事務隔離級別詳解 事務隔離級別概述 MySQL支持四種標準的事務隔離級別,它們定義了事務在并發環境下的可見性規則和可能出現的并發問題: READ UNCOMMITTED(讀未提交) ? 最低隔離級別 ? 事務可以讀取其他事務未提交的數據&…

計算機視覺(CV)技術的優勢和挑戰(本片為InsCode)

計算機視覺(CV)技術是一種利用計算機和算法來模擬人類視覺實現圖像和視頻處理的技術。它在各個領域都有著廣泛的應用,具有許多優勢和挑戰。 優勢: 自動化:CV 技術可以自動識別、分類、跟蹤和分析圖像和視頻數據&…

Android JIT編譯:adb shell cmd package compile選項

Android JIT編譯:adb shell cmd package compile選項 例如: adb shell cmd package compile -m speed -f --full 包名 配置參數指令說明: compile [-r COMPILATION_REASON] [-m COMPILER_FILTER] [-p PRIORITY] [-f] [--primary-dex] …

Android Kotlin 項目集成 Firebase Cloud Messaging (FCM) 全攻略

Firebase Cloud Messaging (FCM) 是 Google 提供的跨平臺消息推送解決方案。以下是在 Android Kotlin 項目中集成 FCM 的詳細步驟。 一、前期準備 1. 創建 Firebase 項目 訪問 Firebase 控制臺點擊"添加項目",按照向導創建新項目項目創建完成后&#x…

搭建PCDN大節點,服務器該怎么配

搭建P2P大節點時,服務器要怎么配呢?需要綜合考慮硬件性能、網絡帶寬、存儲能力、系統架構以及安全性等多個方面,以確保節點能夠高效、穩定地運行。 一、硬件配置 CPU:選擇高性能的多核處理器,以滿足高并發處理需求。核…

(done) 吳恩達版提示詞工程 8. 聊天機器人 (聊天格式設計,上下文內容,點餐機器人)

視頻:https://www.bilibili.com/video/BV1Z14y1Z7LJ/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 別人的筆記:https://zhuanlan.zhihu.com/p/626966526 8. 聊天機器人(Chatbot) …

AtCoder Beginner Contest 403(題解ABCDEF)

A - Odd Position Sum #1.奇數數位和 #include<iostream> #include<vector> #include<stdio.h> #include<map> #include<string> #include<algorithm> #include<queue> #include<cstring> #include<stack> #include&l…

【Game】Powerful——Abandoned Ruins(9)

文章目錄 1、新增古玩2、機關機制3、探索法寶4、智斗強敵5、地圖6、參考 2025 年 1 月迎來的新玩法——荒廢遺跡 每周四個寶藏鏟&#xff08;老玩法&#xff09;或者兩個遺跡線索&#xff08;新玩法&#xff09;&#xff0c;3 個寶藏鏟也可以換一個遺跡線索&#xff0c;之前沒時…

構建網頁版IPFS去中心化網盤

前言&#xff1a;我把它命名為無限網盤 Unlimited network disks&#xff08;ULND&#xff09;&#xff0c;可以實現簡單的去中心化存儲&#xff0c;其實實現起來并不難&#xff0c;還是依靠強大的IPFS&#xff0c;跟著我一步一步做就可以了。 第一步&#xff1a;準備開發環境…

國標GB28181視頻平臺EasyGBS在物業視頻安防管理服務中的應用方案?

一、方案背景? 在現代物業服務中&#xff0c;高效的安全管理與便捷的服務運營至關重要。隨著科技的不斷發展&#xff0c;物業行業對智能化、集成化管理系統的需求日益增長。EasyGBS作為一款基于國標GB28181協議的視頻監控平臺&#xff0c;具備強大的視頻管理與集成能力&#…

[Unity]設置自動打包腳本

背景 我們經常會使用自動打包功能 文件名稱: AutoBuild.csusing System.IO; using System.Linq; using UnityEditor; using UnityEngine;public class AutoBuilder {[MenuItem("Build/GetCurrentBuildTarget")]public static void GetCurrentBuildTarget(){Debug.L…

正點原子STM32H743單片機實現ADC多通道檢測

目標 使用STM32CubeMX工具&#xff0c;配置ADC相關參數&#xff0c;實現在STM32H743單片機上獲取ADC多通道電壓值。共14個ADC引腳&#xff0c;ADC2有5個&#xff0c;ADC3有9個&#xff0c;全部設置單通道 ADC引腳 PF3PF4PF5PF10PC0PC2PC3PH2PH3PA3PB0PB1PA4PA5PA6 STM32cube…

深度學習基礎(四)——計算量(FLOPs)、參數量(Params)、計算速度(FLOPS/TOPS))

一、計算量FLOPs FLOPs&#xff0c;全稱為Floating Point Operations, (s為復數縮寫&#xff09;&#xff0c;浮點運算數&#xff0c;指模型完成一次前向傳播所需的浮點運算次數&#xff0c;可以理解為計算量&#xff08;模型的時間復雜度&#xff09;&#xff0c;用來衡量算法…