線程安全與鎖優化

来源:https://www.cnblogs.com/HuiH/archive/2020/01/27/12236206.html
-Advertisement-
Play Games

1.線程安全:當多個線程訪問一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方法進行任何其他的協調操作,調用這個對象的行為都可以獲得正確的結果,那這個對象就是線程安全的。 2.Java語言中的線程安全 根據線程安全的安全程度由強到弱來排序,我們可以把 ...


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

2.Java語言中的線程安全

 根據線程安全的安全程度由強到弱來排序,我們可以把Java語言中各種操作共用的數據分為以下5類:不可變、絕對線程安全、相對線程安全、線程相容和線程對立。

  1)不可變:不可變的對象一定是線程安全的;Java語言中,如果共用數據是一個基本數據類型,那麼只要在定義時使用final關鍵字修飾它就可以保證它是不可變的。如果共用數據是一個對象,那就需要保證對象的行為不會對其狀態產生任何影響,即把對象中帶有狀態的變數都聲明為final,這樣在構造函數結束之後,它就是不可變的。

  2)絕對線程安全:就是一個類在任何運行時環境下,調用者都不需要任何額外的同步措施。在Java API中標註自己是線程安全的類,大多數都不是絕對線程安全的。

  3)相對線程安全:就是我們通常所講的線程安全,它需要保證對這個對象單獨的操作是線程安全的,我們在調用時不需要做額外的保障措施。但對於一些特定順序的連續調用,就可能需要在調用端使用額外的同步手段來保證調用的正確性。

  4)線程相容:指對象本身並不是線程安全的,但是可以通過在調用端正確地使用同步手段來保證對象在併發環境下可以安全地使用。

  5)線程對立:指無論調用端是否採取同步措施,都無法在多線程環境中併發使用的代碼。

3.線程安全的實現方法

  1)互斥同步:同步是指在多個線程併發訪問共用數據時,保證共用數據在同一時刻只被一個線程使用,而互斥是實現同步的一種手段,臨界區、互斥量和信號量都是主要的互斥實現方式。

  在Java中,最基本的互斥同步手段就是synchronized關鍵字,synchronized關鍵字經過編譯之後,會在同步塊的前後分別形成monitorenter和monitorexit這兩個位元組碼指令,這兩個位元組碼指令都需要一個reference類型的參數來指明鎖定和解鎖的對象。如果Java程式中的synchronized明確指定了對象參數,那就是這個對象的reference;如果沒有明確指定,那就是synchronized修飾的是實例方法還是類方法,去取對應的對象實例或class對象作為鎖對象。

  根據虛擬機規範的要求,在執行monitorenter指令時,首先要嘗試獲取對象鎖。如果這個對象沒有被鎖定,或當前線程已經擁有了那個對象的鎖,把鎖的計數器加1;在執行monitorexit指令時會將鎖計數器減1,當計數器為0時,鎖就被釋放。如果獲取對象鎖失敗,那當前線程就要阻塞等待,直到對象鎖被另一個線程釋放為止。

  synchronized同步塊對同一個線程來說是可重入的,不會出現把自己鎖死的問題。其次,同步塊在已經進入的線程執行完成之前,會阻塞後面其他線程的進入。

  除了synchronized之外,我們還可以使用java.util.concurrent包中的重入鎖(ReentrantLock)來實現同步。他們都具備一樣的線程重入特性,一個表現為API層的互斥鎖(lock和unlock方法配合try/finally語句塊來完成),另一個表現為原生語法層面的互斥鎖。不過相比synchronized,ReentrantLock增加了一些高級功能,主要有:等待可中斷、可實現公平鎖以及鎖可以綁定多個條件。

  等待可中斷是指當持有鎖的線程長期不釋放鎖時,正在等待的線程可以選擇放棄等待,改為處理其他事情。

  公平鎖是指多個線程在等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖。而非公平鎖是在鎖被釋放時,任何一個等待鎖是線程都有機會獲得鎖。synchronized中的鎖就是非公平的,ReentrantLock預設情況下也是非公平的,但可以通過帶有布爾值的構造函數要求使用公平鎖。

  鎖綁定多個條件是指一個ReentrantLock對象可以同時綁定多個Condition對象,只需要多次調用newCondition()方法即可。

  2)非阻塞同步:基於衝突檢測的樂觀併發策略,就是先進行操作,如果沒有其他線程爭用共用資源,那操作就成功了;如果共用數據有爭用,產生了衝突,那就再採取其他的補償措施(最常見的補償措施就是不斷重試,直到成功為止),這種樂觀的併發策略的許多實現都不需要把線程掛起,因此這種同步操作稱為非阻塞同步。

  CAS指令需要有3個參數,分別為記憶體知(V)、舊的預期值(A)、新值(B)。當且僅當V符合舊預期值A時,處理器用新值B更新V值,否則就不執行更新,但是無論是否更新了V值,都會返回V的舊值。上述過程是一個原子操作。CAS存在的問題:如果一個變數V初次讀取時是A值,並且在準備賦值時檢查它仍為A值,那我們就說它的值沒有被其他線程變過了嗎?如果在這段期間曾被改成B,後來又被改回A,那CAS操作就誤以為它沒有被改變過,這個漏洞稱為CAS的ABA問題。JUC包為瞭解決此問題,提供了一個帶有標記的原子引用類,它可以通過控制變數值的版本來保證CAS的正確性。

  3)無同步方案:如果一個方法就不涉及共用數據,那就自然無須任何同步措施去保證同步。

  可重入代碼:可以在代碼執行的任何時刻中斷它,轉而去執行另外一段代碼,而在控制權返回後,原來的程式不會出現任何錯誤。所有可重入的代碼都是線程安全的。判斷代碼是否具備可重入性:如果一個方法,它的返回結果是可以預測的,只要輸入了相同的數據,就能返回相同的結果,那它就滿足可重入性的要求,也就是線程安全的。

  線程本地存儲:如果一個變數要被某個線程共用,可以通過java.util.ThreadLocal類來實現線程本地存儲的功能。

4.鎖優化

  1)自旋鎖與自適應自旋:讓後面請求鎖的那個線程等待一會,但不放棄處理器的執行時間,看看持有鎖的線程是否很快會釋放鎖。為了讓線程等待,我們可以讓線程執行一個忙迴圈(自旋),這項技術就是自旋鎖。如果鎖被占用的時間很短,自旋等待的效果就非常好;反之,鎖占用的時間很長,那麼自旋線程只會白白浪費處理器資源,因此,如果自旋超過了限定的次數仍然沒有獲得鎖,就應將線程掛起。自旋次數預設值為10次。

  JDK1.6之後引入了自適應的自旋鎖,意味著自旋的時間不再固定了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定的。

  2)鎖消除:是指虛擬機即時編譯器在運行時,對一些代碼上要求同步,但是被檢測到不可能存在共用數據競爭的鎖進行消除。鎖消除的主要判定依據來源於逃逸分析的數據支持,如果判斷在一段代碼中,堆上的所有數據都不會逃逸出去從而被其他線程訪問到,那就可以把它們當作棧上數據對待,認為它們是線程私有的,同步加鎖自然就無須進行。

  3)鎖粗化:如果虛擬機探測到一串零碎的操作都對同一個對象加鎖,將會把加鎖同步的範圍擴展(粗化)到整個操作序列的外部。

  4)輕量級鎖:本意是在沒有多線程競爭的前提下,減少傳統的重量級鎖使用操作系統互斥量產生的性能消耗。

  輕量級鎖的執行過程:在代碼進入同步塊時,如果此同步對象沒有被鎖定(鎖標誌位為“01”狀態),虛擬機首先將在當前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於存儲鎖對象目前的運行時數據的拷貝。然後,虛擬機將使用CAS操作嘗試將對象的運行時數據更新為指向鎖記錄的指針。如果這個更新動作成功了,那麼這個線程就擁有了該對象的鎖,並且對象運行時數據的鎖標誌位將轉變為“00”,即表示此對象處於輕量級鎖定狀態。如果這個更新操作失敗了,虛擬機首先會檢查對象的運行時數據是否指向當前線程的棧幀,如果只說明當前線程已經擁有了這個對象的鎖,那就直接進入同步塊繼續執行,否則說明這個鎖對象已經被其他線程搶占了。如果有兩條以上的線程爭用同一個鎖,那輕量級鎖就不再有效,要膨脹為重量級鎖,鎖標誌位的狀態值為“10”,運行時數據中存儲的就是指向重量級鎖(互斥量)的指針,後面等待鎖的線程也要進入阻塞狀態。

  解鎖過程:也是通過CAS操作來進行的,如果對象的運行時數據仍然指向著線程的鎖記錄,那就用CAS操作把對象當前的運行時數據的拷貝替換回來,如果替換成功,整個同步過程就完成了。如果替換失敗,說明有其他線程嘗試獲得該鎖,那就要在釋放鎖的同時,喚醒被掛起的線程。

  5)偏向鎖:偏向鎖就是在無競爭的情況下把整個同步都消除掉。偏向鎖的偏意思是這個鎖會偏向於第一個獲得它的線程,如果在接下來的執行過程中,該鎖沒有被其他的線程獲取,則持有偏向鎖的線程將永遠不需要再進行同步。

  假設當虛擬機啟用了偏向鎖,那麼,當鎖對象第一次被線程獲取時,虛擬機將會把對象頭中的標誌位設為“01”,即偏向模式。同時使用CAS操作把獲取到的這個鎖的線程ID記錄在對象的運行時數據中,如果CAS操作成功,持有偏向鎖的線程以後每次進入這個鎖相關的同步塊時,虛擬機都可以不再進行任何同步操作。當有另外一個線程去嘗試獲取這個鎖時,偏向模式就宣告結束。


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

-Advertisement-
Play Games
更多相關文章
  • 1 Redis客戶端 1.1 自帶命令行客戶端 l 命令格式: ./redis-cli -h 127.0.0.1 -p 6379 l 修改redis配置文件(解決IP綁定問題) # bind 127.0.0.1 綁定的IP才能訪問redis伺服器,註釋掉該配置 protected-mode yes ...
  • Markdown線上編輯器 www.MdEditor.com 這個作業屬於哪個課程 2020年面向對象程式設計 (福州大學 數學與電腦科學學院) 這個作業要求在哪裡 面向對象程式設計寒假作業1 這個作業的目標 安裝C++開發環境(見附錄)、完成問答題、實踐題及編程題、發佈博客 作業正文 問答題、實 ...
  • 單例模式: 即在整個生命周期中,對於該對象的生產始終都是一個,不曾變化。 保證了一個類僅有一個實例,並提供一個訪問它的全局訪問點。 作用: 在要求線程安全的情況下,保證了類實例的唯一性,線程安全。 在不需要多實例存在時,保證了類實例的單一性。不浪費記憶體。 特點: 公有的方法獲取實例, 私有的構造方法 ...
  • 三大查找演算法 1.二分查找(Binary Search) 2.插值查找(InsertValue Search) 3.斐波那契查找(Fibonacci Search) ...
  • Map創建 創建Map: var map1 = {"first":"Dart",1:true,true:"2"}; 創建不可變Map: var map2 = const{"first":"Dart",1:true,true:"2"};構造創建:var map3 = new Map(); 常用操作 [ ...
  • 一、創建list 創建List : var list = [1,2,3,"Dart",true]; 創建不可變List : var list = const [1,2,3,"Dart",true]; 構造創建:var list3 = new List(); 二、常用操作 [],length,add( ...
  • execute(String sql) 可執行任何sql語句,但返回值是void,所以一般用於資料庫的新建、修改、刪除和數據表記錄的增刪改。 int update(String sql) int update(String sql, Object...args) 增刪改,args傳遞實參,返回受影響 ...
  • 3. 如果同時拿一個板塊股票的收市價和成交額 前一篇說到,用大盤指數,如恆生指數,上證,深證,這些重要的大盤指數來做Dataframe主鍵,那麼如果是同時拿一個板塊股票的收市價和成交額,可以怎樣操作呢。 在實際開發中,應該是簡單的數據結構,容易閱讀為主,所以Dataframe的multi index ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...