18 Java記憶體模型與線程_JVM同步機制和鎖類庫實現線程安全

来源:https://www.cnblogs.com/knowledgeispower/archive/2022/12/16/16987417.html
-Advertisement-
Play Games

1 線程安全定義 含糊的定義:如果一個對象可以安全地被多個線程同時使用,那它就是線程安全的 嚴謹的定義: 當多個線程同時訪問一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行為都可以獲得正確的結果,那就稱這個對 ...


目錄

1 線程安全定義

含糊的定義:如果一個對象可以安全地被多個線程同時使用,那它就是線程安全的
嚴謹的定義:

當多個線程同時訪問一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行為都可以獲得正確的結果,那就稱這個對象是線程安全的。-----From《Java併發編程實戰》作者Brian Goetz

2 Java數據與線程安全

從線程安全形度,將Java中各種操作共用的數據分為:不可變、絕對線程安全、相對線程安全、線程相容和線程對立

2.1 不可變

不可變的對象一定是線程安全。

如何保證不可變呢?

  1. 基本數據類型,用final修飾
  2. 對象類型:用final修飾對象中可變的欄位

Java中常用的不可變對象:String、Number的部分子類如 Long、Double、BigInteger、BigDecimal等。

為什麼String不可變,參考:基本數據類型及String

2.2 絕對線程安全

ConcurrentHashMap (我後續會更新ConcurrentHashMap源碼分析專題)

2.3 相對線程安全

定義:只能保證對象單次的操作是線程安全,連續調用不能保證線程安全
大部分聲稱線程安全的類都屬於這種類型,例如Vector、HashTable、Collections的 synchronizedCollection()方法包裝的集合等。

2.4 線程相容

定義:對象本身並不是線程安全的,但是可以通過在調用端使用同步手段來保證對象在併發環境中可以安全地使用。如集合類ArrayList和HashMap等。

2.5 線程對立

定義:不管調用端是否採取了同步措施,都無法在多線程環境中併發使用代碼。
例子:Thread類的suspend()和resume()方法:如果有兩個線程同時持有一個線程對象,一個嘗試去中斷線程,一個嘗試去恢複線程,在併發進行的情況下,無論調用時是否進行了同步,目標線程都存在死鎖風險

3 Java線程安全支持

JVM同步機制鎖類庫實現線程安全

3.1 互斥同步

同步:在多個線程併發訪問共用數據時,保證共用數據在同一個時刻只被一條線程使用
互斥:實現同步的一種手段。
互斥同步性能開銷:互斥同步屬於一種悲觀的併發策略,無論共用的數據是否真的會出現競爭,它都會進行加鎖,引發:用戶態到核心態轉換、維護鎖計數器、檢查是否有被阻塞的線程需要被喚醒 等開銷

3.1.1 synchronized互斥同步原理

  1. synchronized關鍵字經過Javac編譯之後,會在同步塊的前後分別形成monitorenter和monitorexit這兩個位元組碼指令。
  2. 執行monitorenter指令時,首先要去嘗試獲取對象的鎖。如果這個對象沒被鎖定,或者當前線程已經持有了那個對象的鎖,就把鎖的計數器的值增加1
  3. 執行 monitorexit指令時會將鎖計數器的值減1,一旦計數器的值為零,鎖隨即就被釋放
  4. 如果獲取對象鎖失敗,那當前線程阻塞等待,直到鎖被釋放。

關於同步塊的說明:
monitorenter和monitorexit指令,都需要一個reference類型的參數,指明要鎖定和解鎖的對象。synchronized修飾地方不同,reference取不同的值:

  • 修飾 對象,取這個對象的引用作為reference;
  • 修飾 實例方法,取方法所屬對象實例作為reference,
  • 修飾 類方法,取Class對象來作為線程要持有的鎖

根據兩個monitorenter和monitorexit這兩個位元組碼指令執行過程,可以得出以下推論:

  • 被synchronized修飾的同步塊對同一條線程來說是可重入
  • 被synchronized修飾的同步塊在持有鎖的線程執行完畢並釋放鎖之前,會無條件地阻塞後面其他線程的進入

3.1.2 Lock介面和ReentrantLock互斥同步原理

參考:JUC鎖: LockSupport詳解 JUC鎖: ReentrantLock詳解 這兩個專題講解

3.1.3 synchronized和Lock對比

Lock應該確保在finally塊中釋放鎖,否則一旦同步代碼塊中拋出異常,則有可能永遠不會釋放持有的鎖。Lock必須由程式員來保證鎖釋放,而synchronized由Java虛擬機來確保即使出現異常,鎖也能被自動釋放。

3.2 非阻塞同步

非阻塞同步:樂觀併發策略:不管風險,先進行操作,如果沒有其他線程爭用共用數據,那操作就直接成功了。如果共用的數據的確被爭用,產生了衝突,那再進行其他的補償措施,最常用的補償措施是不斷地重試,直到出現沒有競爭的共用數據為止。樂觀併發策略的實現不再需要把線程阻塞掛起

利用處理器指令,實現非阻塞同步

硬體保證某些從語義上看起來需要多次操作的行為可以只通過一條處理器指令就能完成,這類指令常用的有:

  • 測試並設置(Test-and-Set)
  • 獲取並增加(Fetch-and-Increment)
  • 交換(Swap)
  • 比較並交換(Compare-and-Swap :CAS)
  • 載入鏈接/條件儲存(Load-Linked/Store-Conditional:LL/SC)

CAS的專題分析:JUC原子類: CAS, Unsafe和原子類詳解

3.3 無同步方案

如果方法不涉及共用數據,那自然不需要採用同步措施來保證其正確性。因此會有一些代碼天生就是線程安全的

3.3.1 可重入代碼

代碼定義:可以在代碼執行的任何時刻中斷它,轉而去執行另外一段代碼(包括遞歸調用它本身),而在控制權返回後,原來的程式不會出現任何錯誤,也不會對結果有所影響。

特征:

  • 不依賴全局變數、堆數據、公用的系統資源
  • 用到的狀態量都由參數中傳入
  • 不調用非可重入的方法

3.3.2 線程本地存儲

代碼定義:共用數據保證只在同一個線程中執行
場景:消費隊列的架構模式(如“生產者-消費者”模式)都會將產品的消費過程限制在一個線程中消費完。

如果變數要被多線程訪問,使用volatile關鍵字將它聲明為“易變的”;如果變數線程獨享,通過java.lang.ThreadLocal類來實現線程本地存儲的功能。

ThreadLocal專題分析:Threadlocal源碼解讀


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我要說的是我們改變 num屬性 的類型,無論是由 Integer改成Long,還是由Long改成Integer,只要num的值在Integer取值範圍內,就不會影響hessian序列化。 ...
  • 股票分析 需求:股票分析 使用tushare包獲取某股票的歷史行情數據。 輸出該股票所有收盤比開盤上漲3%以上的日期。 輸出該股票所有開盤比前日收盤跌幅超過2%的日期。 假如我從2010年1月1日開始,每月第一個交易日買入1手股票,每年最後一個交易日賣出所有股票,到今天為止,我的收益如何? impo ...
  • 前言 明天就是擁抱情人節,情侶們會在公開的場合擁抱,向世人宣告你倆的愛意,也讓這個寒冷的冬天變得格外溫馨。到了年底依然能熱情擁抱,也見證了兩人情意如昔。 今天子川就給大家帶來就是的利用Python製作表白神器,記得發給自己的心儀對象。廢話不多說直接開整~ 開發工具 Python版本: 3.6 相關模 ...
  • 本文介紹如何使用 Pandas Profiling 的比較報告功能,分析兩個數據集的分佈差異,完成數據探索分析 (EDA) 的完整流程,為後續分析做準備。 ...
  • 作者:耿宗傑 前言 關於pprof的文章在網上已是汗牛充棟,卻是千篇一律的命令介紹,鮮有真正實操的,本文將參考Go社區資料,結合自己的經驗,實戰Go程式的性能分析與優化過程。 優化思路 首先說一下性能優化的一般思路。系統性能的分析優化,一定是從大到小的步驟來進行的,即從業務架構的優化,到系統架構的優 ...
  • 前言 網易雲的Vip音樂下載下來,格式不是mp3/flac這種通用的音樂格式,而是經過加密的ncm文件。只有用網易雲的音樂App才能夠打開。於是想到可不可以把.ncm文件轉換成mp3或者flac文件,上google查了一下,發現有不少人已經做了這件事,但沒有發現C語言版本的,就想著寫一個純C語言版本 ...
  • pandas 為什麼學習pandas numpy已經可以幫助我們進行數據的處理了,那麼學習pandas的目的是什麼呢? numpy能夠幫助我們處理的是數值型的數據,當然在數據分析中除了數值型的數據還有好多其他類型的數據(字元串,時間序列),那麼pandas就可以幫我們很好的處理除了數值型的其他數據! ...
  • 大家好,咱們前面通過十篇的文章介紹了docker的基礎篇,從本篇開始,咱們的《docker學習系列》將要進入到高級篇階段(基礎篇大家可以查看之前發佈的文章)。 咱們先來介紹:docker複雜方式安裝軟體。通過按照mysql\redis兩個案例來講解 Docker複雜安裝說明,兩個案例: 1:安裝my ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...