線程安全與鎖優化

来源: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
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...