線程安全的產生以及解決方案

線程安全

原子性(Atomicity)、可見性(Visibility)、有序性(Ordering)?是保證線程安全的三大核心要素 —— 線程安全問題的本質,幾乎都是這三個特性中的一個或多個被破壞導致的。

  • 操作不會被 “中途打斷”(原子性);
  • 操作結果能被其他線程 “及時看見”(可見性);
  • 操作順序符合 “語義邏輯”(有序性)。

可見性:

可見性問題指:一個線程對共享變量的修改,其他線程可能無法立即看到,甚至永遠看不到

為什么影響線程安全?
若可見性被破壞,線程會基于 “舊值” 做決策或修改,導致邏輯錯誤。

現代 CPU 為提升效率,引入了多級緩存(L1、L2、L3),線程的 “工作內存” 本質是 CPU 緩存的抽象。當線程修改變量時:

  • 步驟 1:修改 CPU 緩存中的副本(工作內存)。
  • 步驟 2:CPU 會在 “合適的時機”(而非立即)將緩存中的新值刷新到主內存(如緩存滿了、發生緩存一致性協議觸發時)。

這就導致:若線程 A 剛修改了變量但未刷新到主內存,線程 B 從主內存讀取的仍是舊值,出現可見性問題。

可見性問題的產生

當線程 A 修改了共享變量?A?時,會先更新自己的工作內存,再異步刷新到主內存(這個過程有延遲)。若此時線程 B 讀取變量?A,可能直接從自己的工作內存中獲取未被更新的老值(因為線程 A 的修改還未同步到主內存,或線程 B 未從主內存重新加載),導致兩個線程看到的變量值不一致。

// 共享變量
private static boolean flag = false;// 線程1:修改flag
new Thread(() -> {flag = true; // 修改工作內存中的flag,尚未同步到主內存System.out.println("線程1已修改flag為true");
}).start();// 線程2:讀取flag
new Thread(() -> {while (!flag) { // 可能一直讀取自己工作內存中的老值(false),陷入死循環// 等待flag變為true}System.out.println("線程2檢測到flag為true");
}).start();

線程 2 可能永遠看不到線程 1 對?flag?的修改,因為?flag?的更新未及時同步到主內存,或線程 2 未重新從主內存加載。

volatile解決:

volatile?保證可見性的核心邏輯是:強制線程對變量的讀寫操作直接與主內存交互,跳過工作內存(CPU 緩存)的緩存優化,具體通過以下兩步實現:

  1. 寫操作時:立即刷新到主內存,并使其他線程的緩存失效
    當線程修改一個?volatile?變量時,JVM 會觸發兩個動作:

    • 強制將工作內存中該變量的新值立即刷新到主內存(不等待 CPU 緩存的 “合適時機”)。
    • 通過 CPU 的緩存一致性協議(如 MESI 協議),通知其他線程中該變量的緩存副本失效(其他線程再讀取時必須從主內存重新加載)。

    類比:volatile?變量的寫操作相當于 “寫完立即把筆記本內容抄回公共白板,并擦掉其他人筆記本上的舊內容”。

  2. 讀操作時:必須從主內存重新加載
    當線程讀取?volatile?變量時,JVM 會強制線程放棄工作內存中的緩存副本,直接從主內存加載最新值。

    類比:volatile?變量的讀操作相當于 “每次看內容前,都先扔掉自己的筆記本,重新從公共白板抄最新內容”。

private static volatile boolean flag = false; // 用volatile修飾// 線程1修改后,會立即刷新到主內存,并使線程2的緩存失效
// 線程2讀取時,會從主內存重新加載,感知到flag的最新值

synchronized解決

核心機制是加鎖和解鎖時的內存同步操作

  1. 加鎖時(進入同步塊)
    線程會清空自己的工作內存,并從主內存重新加載共享變量的最新值到工作內存。
    (類比:線程進入同步塊前,先把自己的 “筆記本” 清空,重新從 “公共白板” 抄最新內容。)

  2. 解鎖時(退出同步塊)
    線程會將工作內存中修改后的共享變量值強制刷新到主內存
    (類比:線程退出同步塊時,必須把 “筆記本” 的修改立即抄回 “公共白板”。)

  3. happens-before 原則
    對同一個鎖,解鎖操作 happens-before 后續的加鎖操作。即:前一個線程解鎖時刷新到主內存的變量值,后一個線程加鎖時必然能從主內存讀到這個最新值。

// 共享變量(無volatile)
private static boolean flag = false;public static void main(String[] args) {// 線程1:修改flagnew Thread(() -> {synchronized (Test.class) { // 加鎖:從主內存加載flag(初始false)flag = true; // 修改工作內存中的flag} // 解鎖:將flag=true刷新到主內存}).start();// 線程2:讀取flagnew Thread(() -> {while (true) {synchronized (Test.class) { // 加鎖:從主內存加載flag的最新值if (flag) {System.out.println("線程2讀取到flag=true");break;}} // 解鎖:無修改,不影響}}).start();
}
  • 線程 1 解鎖時,flag=true?被強制刷新到主內存。
  • 線程 2 每次加鎖時,都會從主內存重新加載?flag,因此必然能感知到?flag?的修改,最終退出循環。

有序性

有序性指程序執行順序符合代碼的 “語義邏輯順序”,避免編譯器 / CPU 的指令重排序破壞線程間的依賴關系。

為什么影響線程安全?
重排序可能打破線程間的 “操作先后依賴”,導致基于順序的邏輯判斷失效。

// 共享變量
private static int a = 0;
private static boolean flag = false;// 線程1:先初始化a,再標記flag
new Thread(() -> {a = 1;      // 操作1:初始化aflag = true; // 操作2:標記a已初始化
}).start();// 線程2:基于flag判斷a是否可用
new Thread(() -> {if (flag) { // 若flag=true,認為a已初始化System.out.println(a); // 可能輸出0(因重排序)}
}).start();

若線程 1 的操作 1 和操作 2 被重排序(先執行?flag=true,再執行?a=1),線程 2 會在?a?未初始化時讀取,輸出 0(不符合預期),破壞線程安全。

volatile解決有序性:

volatile?通過在變量的讀寫操作前后插入內存屏障(Memory Barrier)?來禁止特定類型的重排序,從而保證有序性。內存屏障是一種特殊的指令,它會阻止編譯器和 CPU 對屏障兩側的指令進行重排序。

volatile 內存屏障的具體規則

操作類型內存屏障插入位置作用
寫操作(v = x)寫操作前插入?StoreStore 屏障禁止當前寫操作與之前的其他寫操作重排序(確保之前的寫操作先于當前寫操作執行)。
寫操作后插入?StoreLoad 屏障禁止當前寫操作與之后的讀 / 寫操作重排序(確保當前寫操作完成后,再執行后續操作)。
讀操作(x = v)讀操作前插入?LoadLoad 屏障禁止當前讀操作與之前的其他讀操作重排序(確保之前的讀操作先于當前讀操作執行)。
讀操作后插入?LoadStore 屏障禁止當前讀操作與之后的寫操作重排序(確保當前讀操作完成后,再執行后續寫操作)。

這些屏障的核心作用是 “隔離屏障兩側的指令”,確保?volatile?變量的讀寫操作不會與其他指令 “交叉執行”。

synchronized解決有序性:?

synchronized?通過?“happens-before 原則”?和?“同步塊的邊界約束”?保證有序性。其核心邏輯是:同步塊內的操作會被視為一個 “不可分割的整體”,不會與同步塊外的操作重排序,且后續線程進入同步塊時,能看到之前同步塊內的所有操作結果。

  1. 同步塊內的操作不會被重排序到塊外
    JMM 規定:編譯器和 CPU 不得將同步塊內的指令重排序到同步塊外部(無論是進入塊前還是退出塊后)。例如:

    synchronized (lock) { // 加鎖a = 1;    // 同步塊內操作1flag = true; // 同步塊內操作2
    } // 解鎖

    編譯器和 CPU 不能將?a=1?或?flag=true?重排序到?synchronized?塊外部,確保同步塊內的操作順序嚴格按代碼執行。

  2. happens-before 關系保證跨線程可見性與順序性
    JMM 的 happens-before 原則規定:對同一個鎖的解鎖操作 happens-before 后續的加鎖操作。即:

    • 線程 A 退出同步塊(解鎖)時,其在同步塊內的所有操作(如?a=1flag=true)都會被刷新到主內存。
    • 線程 B 進入同步塊(加鎖)時,會從主內存加載所有變量的最新值,因此能看到線程 A 在同步塊內的所有操作結果。

    這種關系確保了:線程 A 的操作 “先行發生于” 線程 B 的操作,兩者的執行順序在邏輯上是有序的。

原子性:

原子性指一個操作(或多個操作的組合)要么全部執行,要么全部不執行,不會被其他線程 “打斷”,中間狀態不會被暴露

典型案例:非原子操作的風險

以?count++?為例,這是一個看似簡單的操作,但在底層會被拆分為 3 個步驟:

  1. 讀取:從主內存讀取?count?的當前值到線程的工作內存。
  2. 修改:在工作內存中對?count?加 1。
  3. 寫入:將修改后的值刷新回主內存。

當兩個線程同時執行?count++?時,可能出現以下交叉執行的情況:

  • 線程 A 讀取?count=0?→ 線程 B 讀取?count=0(此時兩者都在步驟 1)。
  • 線程 A 加 1 后?count=1?→ 線程 B 加 1 后?count=1(步驟 2)。
  • 線程 A 寫入主內存?count=1?→ 線程 B 寫入主內存?count=1(步驟 3)。

基于鎖機制解決:

private int count = 0;// 用synchronized修飾方法,保證count++的原子性
public synchronized void increment() {count++; // 復合操作被synchronized保護,不會被其他線程中斷
}

線程進入?synchronized?方法 / 塊時必須獲取鎖,執行完成后釋放鎖。同一時間只有一個線程能持有鎖,確保臨界區內的操作不會被其他線程打斷。

使用原子類:

CAS 機制Atomic?系列類):通過硬件指令實現無鎖原子操作,適合簡單場景,性能更優。

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

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

相關文章

Spring Cloud Netflix學習筆記01

文章目錄前言一、微服務概述什么是微服務?微服務與微服務架構微服務優缺點優點缺點微服務技術棧有那些?二.SpringCloud入門概述SpringCloud是什么?SpringCloud和SpringBoot的關系Dubbo 和 SpringCloud技術選型總結SpringCloud能干嘛&#xff…

專題:2025母嬰行業消費洞察與分齡營養趨勢報告|附40 +份報告PDF、交互圖表數據匯總下載

原文鏈接:https://tecdat.cn/?p43654 當95后媽媽拿著計算器對比DHA純度,當爸爸們為“防紅屁屁紙尿褲”貨比三家,母嬰行業的風向早就變了。從“一把奶粉喂到3歲”到“按月齡定制營養包”,從“進口就好”到“看專利數據下單”&…

redhat6/centos6 配置yum源

由于RHEL6/centos6系統官方早就停止通知維護了,公司的開發服務器有比較老,發現竟然scp都沒有裝。。。今天配置個本地yum源,安裝一下常規軟件和開發環境比較簡單,直接上代碼1.上傳一個centos6的iso文件CentOS-6.5-x86_64-bin-DVD1.…

day31 SQLITE

數據庫相關函數數據庫創建int sqlite3_open( const char *filename, sqlite3 **ppDb);功能:打開數據庫,不存在則創建參數:const char *filename 數據庫名sqlite3 **ppDb 二級指針,傳出ppDb數據庫的一級指…

嵌入式-SPI番外之按鈕驅動程序的編寫-Day15

目錄 一、按鈕簡單操作回憶 二、按鈕新操作實現 (1)按鈕的點擊實現燈亮/滅 ①連接電路 ②初始化板載LED和按鈕 ③按鈕程序的基本原理(核心仍為0亮/1滅) ④按鈕消抖的原理 三、按鈕封裝的操作-點擊,雙擊&#xf…

星域智鏈科技:用科技點亮生活,以 AI 拓展無限可能

星域智鏈科技(東莞市)有限公司簡介 星域智鏈科技(東莞市)有限公司,理念是 讓科技便利生活、豐富生活,專注于科技、AI領域。 全場景 GPS 定位器 —— 精準追蹤,守護安全,適用于車輛…

國內代理IP在SEO行業中的應用

隨著互聯網的快速發展,SEO(搜索引擎優化)已經成為了數字營銷的重要組成部分。無論是企業還是個人站長,都希望通過SEO提升自己網站的排名和流量。然而,隨著競爭的激烈,傳統的SEO優化手段已經逐漸顯現出局限性…

Linux + arm 內存屏障

ARM 硬件層的屏障指令DMB (Data Memory Barrier):保證在它之前的內存訪問(符合給定域/類型)在它之后的內存訪問之前對可見性排序。常用域:ish(Inner Shareable),sy(system-wide&…

網絡安全中的遠程控制活動檢測與防御策略

本文還有配套的精品資源,點擊獲取 簡介:遠程控制技術在IT領域中用于網絡連接和設備操作,但同樣被黑客利用進行非法入侵。端口占用情況是識別遠程控制活動的關鍵因素,使用工具如"cports"可以監控這些端口。系統中未知…

UIGestureRecognizer 各個子類以及其作用

在 iOS 里,UIGestureRecognizer 是一個抽象基類,專門用來處理手勢事件。它本身不能直接用,必須用它的 子類。這些子類分別對應常見的手勢識別器。常見的 UIGestureRecognizer 子類及作用1. UITapGestureRecognizer作用:點擊手勢&a…

計算機網絡 HTTPS 全流程

HTTPS 通信的全流程(特別是 TLS 握手階段)中使用的三個隨機數是保障安全性的核心設計,不能隨意減少。每個隨機數都承擔著至關重要的安全職責。下面詳細解釋 HTTPS 全流程,并重點分析這三個隨機數的作用和必要性:&#…

DL00271-基于YOLOv11的激光雷達LiDAR船舶目標檢測含完整數據集

【CSDN推薦】基于YOLOv11的激光雷達(LiDAR)船舶目標檢測——含完整數據集!🚢 科研人員必看! 高校老師、學生和研究者們,前沿技術來了!本論文利用YOLOv11模型,結合激光雷達&#xff0…

SQL-leetcode—3374. 首字母大寫 II

3374. 首字母大寫 II 表:user_content -------------------- | Column Name | Type | -------------------- | content_id | int | | content_text| varchar | -------------------- content_id 是這張表的唯一主鍵。 每一行包含一個不同的 ID 以及對應的文…

告別籠統的 200 OK:一份給 API 設計者的 HTTP 狀態碼終極指南

文章目錄寫在前面問題描述核心結論與建議簡要描述詳細闡述1xx - 信息性響應 (Informational)2xx - 成功 (Successful)3xx - 重定向 (Redirection)4xx - 客戶端錯誤 (Client Error)5xx - 服務器錯誤 (Server Error)HTTP 狀態碼速查表參考以及更多更詳細的狀態碼查詢寫在前面 你…

從防抖節流到鏈表樹:編程世界中的抽象優化藝術

從防抖節流到鏈表樹:編程世界中的抽象優化藝術 在編程的知識體系中,有些概念看似毫不相關,卻在底層邏輯上有著驚人的相似之處。防抖與節流、鏈表與樹,這兩組分屬不同領域的概念,正是這種思維共性的典型代表。它們不僅展…

第三階段數據-3:數據庫腳本生成,備份與還原,分離與附加

1_生成數據庫腳本(1)在數據庫上右鍵選擇任務(2)選擇生成腳本(3)選擇下一步,如果下次不想顯示此頁面,可勾選不再顯示此頁(4)如果導出全部數據,選擇…

React框架超詳細入門到實戰項目演練【前端】【React】

React框架 1.前端展示解釋 當客戶端訪問服務器時,會從服務器中下載很多靜態文件到本地,比如css、js等前端渲染文件 下載完成之后瀏覽器會將這些文件組合形成前端頁面渲染出來。 2.React概述 React是一個專注于構建用戶界面的JavaScript庫,…

本地部署的終極多面手:Qwen2.5-Omni-3B,視頻剪、音頻混、圖像生、文本寫全搞定

Qwen2.5-Omni-3B是什么? Qwen2.5-Omni-3B 是由阿里巴巴 Qwen 團隊推出的一款輕量級多模態大模型,作為 Qwen2.5-Omni-7B 的高效優化版本,專為消費級硬件環境量身打造。該模型具備處理文本、音頻、圖像和視頻等多種模態輸入的能力,…

連續空間強化學習:策略輸出的兩種形態 —— 概率分布與確定性動作

在強化學習的世界里,智能體與環境的交互核心是 “動作選擇”。當面對離散動作空間(如圍棋的落子點、游戲的按鍵操作)時,智能體可以直接枚舉或概率選擇有限的動作;但在連續動作空間中(如機器人關節角度、無人…

IT運維背鍋權限泄露?集中式管控如何化解風險?

在企業數字化轉型的浪潮中,IT運維團隊常常被推到風口浪尖。員工離職后權限未及時回收、賬號共享導致數據泄露、跨系統權限配置不一致……這些問題一旦暴露,IT運維往往成為“背鍋俠”。權限泄露不僅威脅企業數據安全,還可能導致合規性風險&…