1. 程式人生 > >【JVM】執行緒安全與鎖優化

【JVM】執行緒安全與鎖優化

執行緒安全

1.定義

當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲得正確的結果

2.分類

(1)不可變

不可變的物件一定是執行緒安全的,只要一個不可變物件被正確地構建出來(沒有發生this引用逃逸情況),那其外部的可見狀態永遠也不會改變 ,永遠也不會看到它在多個執行緒之中處於不一致的狀態。如Integer類,內部狀態變數value定義為final來保障狀態不變。

例1: JDK 中 Integer 類的建構函式

   private final int value;  

  /** 
   * Constructs a newly allocated {@code Integer} object that 
   * represents the specified {@code int} value. 
   * 
   * @param
value the value to be represented by the * {@code Integer} object. */
public Integer(int value) { this.value = value; }

(2)絕對執行緒安全

要達到不管執行的環境如何,呼叫者都不需要任何額外的同步措施。在Java API中標註自己是執行緒安全的類,大多數都不是絕對執行緒安全的。

例2:加入同步以保證 Vector 訪問的執行緒安全性

Thread removeThread = new
Thread(new Runnable() { @Override public void run() { synchronized(vector) { for (int i = 0; i < vector.size(); i++) { vector.remove(i); } } } }); Thread printThread = new Thread(new Runnable() { @Override
public void run() { synchronized(vector) { for (int i = 0; i < vector.size(); i++) { System.out.println(vector.get(i)); } } } });

(3)相對執行緒安全

通常意義上的執行緒安全,需要保證對這個物件的單獨操作是執行緒安全的,在呼叫的時候不需要做額外的保障措施,但對於一些特定順序的連續呼叫,就可能需要在呼叫端使用額外的同步手段來保證呼叫的正確性。如Vector、HashTable、Collections的synchronizedCollection()方法包裝的集合。

(4)執行緒相容

指物件本身不是執行緒安全的,但是可以通過呼叫端正確地使用同步手段來保證物件在併發環境中可以安全地使用,Java API大部分類屬於執行緒相容,如ArrayList、HashMap。

(5)執行緒對立

無論呼叫端是否採取了同步措施,都無法在多執行緒環境下併發使用的程式碼。有害的,應該儘量避免,如Thread類的suspend()和resume()方法,如果兩個執行緒同時持有一個執行緒物件,一個嘗試去中斷執行緒,一個嘗試去恢復執行緒,並且併發進行,無論呼叫時是否進行了同步,目標執行緒都存在死鎖風險,所以這兩個方法已經被宣告廢棄。

鎖優化

1.定義

在阻塞式的情況下,如何讓效能不要變得太差

2.鎖的分類

鎖分為:輕量級鎖、自旋鎖、偏向鎖、

  • 物件頭:描述物件的hash、鎖資訊、垃圾回收標記。指向鎖記錄的指標、指向monitor的指標、GC標記、偏向鎖執行緒ID

(1)偏向鎖:鎖會偏向於當前已經佔有鎖的執行緒

  • 將物件頭Mark的標記設定為偏向,並將執行緒ID寫入物件頭Mark

  • 只要沒有競爭,獲取偏向鎖的執行緒,在將來進入同步塊時,不需要同步

  • 當其他執行緒請求相同的鎖時,偏向模式結束

  • -XX:+UseBiasedLocking 預設啟用

  • 在競爭激烈的場合,偏向鎖會增加系統的負擔

(2)輕量級鎖

  • 在程式碼進入同步塊的時候,如果此同步物件沒有被鎖定,虛擬機器首先將在當前執行緒的棧幀中建立一個名為鎖記錄的空間,用於儲存鎖物件目前的Mark Word的拷貝,稱為Displaced Mark Word,

  • 然後使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標,如果更新動作成功,那麼執行緒就擁有了該物件的鎖,並且物件Mark Word的鎖標誌位轉變成“00”,表示處於輕量級鎖定狀態。

  • 如果更新動作失敗,先檢查物件的Mark Word是否指向當前執行緒的棧幀,如果是,就可以直接進入同步塊繼續執行,否則說明這個鎖物件被其他執行緒搶佔了,如果有兩條以上執行緒爭用同一個鎖,那輕量級鎖就不再有效,要膨脹為重量級鎖,鎖標誌的狀態值轉變為“10”。

  • 解鎖過程也是通過CAS操作進行,如果物件的Mark Word仍然指向執行緒的鎖記錄,就用CAS操作把物件當前的Mark Word和執行緒中複製的Displaced Mark Word替換回來,

  • 如果替換成功,整個同步過程就完成了,如果替換失敗,說明有其他執行緒嘗試過獲取鎖,那就要在釋放鎖的同時,喚醒被掛起的執行緒。

  • 輕量級鎖使用CAS操作避免使用互斥量的開銷,如果存在鎖競爭,除了互斥量的開銷,還額外發生了CAS操作。

(3)自旋鎖

  • 若執行緒可以很快獲得鎖,不用在OS掛起鎖,讓執行緒自旋。

  • 共享資料的鎖定狀態可能只持續很短的一段時間,為了這段時間去掛起和恢復執行緒不值得。如果物理機器有一個以上的處理器,能讓兩個或以上的執行緒同時並行執行,可以讓後面請求鎖的執行緒“等待一段時間”,但不放棄處理器的執行時間,看看持有鎖的執行緒是否很快釋放鎖,為了讓執行緒等待,讓執行緒執行一個忙迴圈(自旋),這項技術就是自旋鎖。

  • JDK 1.6引入自適應自旋,自旋時間不固定,由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定。如果在同一個鎖物件上,自旋等待剛剛成功獲得過鎖,並且持有鎖的執行緒正在執行,那麼虛擬機器會認為這次自旋也很可能再次成功,進而允許自旋等待持續相對更長的時間,另外,如果對於某個鎖,自旋很少成功,那麼在以後獲取這個鎖時將可能省略掉自旋過程,以避免浪費處理器資源。

(4)內置於JVM中獲取鎖的步驟

① 偏向鎖可用會嘗試偏向鎖

② 輕量級鎖可用會嘗試輕量級鎖

③ 若①②失敗,嘗試自旋鎖

④ 若①②③都失敗,嘗試普通鎖

圖解:

這裡寫圖片描述

2.鎖優化的方法

(1)減少鎖的持有時間(方法鎖改為鎖物件)

例1

優化前:在進入方法前就要得到鎖,其他執行緒就要在外面等待

public synchronized void syncMethod(){  
        othercode1();  
        mutextMethod();  
        othercode2(); 
    }

優化後:減少其他執行緒等待的時間

public void syncMethod(){  
        othercode1();  
        synchronized(this)
        {
            mutextMethod();  
        }
        othercode2(); 
    }

(2)減少鎖粒度:將大物件(這個物件可能會被很多執行緒訪問),拆成小物件,大大增加並行度,降低鎖競爭

例:concurrentHashMap的分段鎖

(3)鎖分離:讀寫鎖

例:LinkedBlockingQueue

這裡寫圖片描述

從頭部取出,從尾部放資料

(4)鎖粗化:使用完公共資源後,立即釋放鎖

如果一系列的連續操作都對同一個物件反覆加鎖和解鎖,甚至加鎖出現在迴圈體中,那即使沒有執行緒競爭,頻繁地進行互斥同步操作也會導致不必要的效能損耗。如果虛擬機器檢測到有一串零碎的操作都對同一個物件加鎖,將會把加鎖同步的範圍擴充套件到整個操作序列的外部。

例2:

優化前:

public void demoMethod(){  
        synchronized(lock){   
            //do sth.  
        }  
        //做其他不需要的同步的工作,但能很快執行完畢  
        synchronized(lock){   
            //do sth.  
        } 
    }

優化後:

public void demoMethod(){  
        //整合成一次鎖請求 
        synchronized(lock){   
            //do sth.   
            //做其他不需要的同步的工作,但能很快執行完畢  
        }
    }

(5)鎖清除:

指在虛擬機器即時編譯器執行時,對一些程式碼上要求同步,但被檢測到不可能存在共享資料競爭的鎖進行消除,判定依據來源於逃逸分析的資料支援,如果一段程式碼,堆上的所有資料都不會逃逸出去被其他執行緒訪問到,那就可以把它們當做棧上資料對待,認為是執行緒私有的,同步加鎖無需進行。

(6)無鎖:無鎖的實現方式(CAS)



本人才疏學淺,若有錯,請指出
謝謝!

相關推薦

JVM執行安全優化

執行緒安全 1.定義 當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲得正確的結果 2.分類 (1)不可變 不可變的物件一定是執行緒安

Java虛擬機器執行安全優化

執行緒安全與鎖優化 絕對執行緒安全 相對執行緒安全 執行緒安全的實現方式 互斥同步 非阻塞同步 鎖優化 參考 絕對執行緒安全 當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境

十二、JVM(HotSpot)執行安全優化----終結篇

注:本博文主要是基於JDK1.7會適當加入1.8內容。 執行緒安全:當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為可以獲取正確的結果,那這個物件就是執行緒安全的。 1、Ja

jvm(13)-執行安全優化

// Atomic 變數自增運算測試(incrementAndGet 方法的原子性) public class AtomicTest { public static AtomicInteger race = new AtomicInteger(0); public static

JVM學習篇(4)之執行安全優化

執行緒安全與鎖優化 Java中執行緒安全 對共享資料的操作 1.        不可變: 不可變的物件一定是執行緒安全的。如String類。 2.        絕對執行緒安全 3.        相對執行緒安全 4.        執行緒相容 5.        執行緒對

深入理解JVM——執行安全優化

執行緒安全 當多個執行緒訪問一個物件時,如果不考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲得正確的結果,那麼這個物件是執行緒安全的。 Java語言中的執行緒安全 執行緒

Linux執行安全可重入函式

【Linux學習】:在Linux的一段時間學習中,剛開始是模糊的,所以很久沒有進行部落格的整理,直到最近自己把Linux的學習從前往後回憶與聯絡清楚了,覺得是時候整理成部落格,變為自己的學習筆記了,先從執行緒安全和可重入函式整理,一方面是趁熱打鐵,另一方面是在這

jvm筆記07:執行安全優化

 java語言中的執行緒安全         按照執行緒安全的”安全程度”由強至弱來排序,我們可以將java語言中各種操作共享的資料分為以下5類:不可變、絕對執行緒安全、相對執行緒安全、執行緒相容和執行緒對立 不可變          不可變的的物件一定是執行緒安全的。無論

一夜搞懂 | JVM 執行安全優化

前言 本文已經收錄到我的 Github 個人部落格,歡迎大佬們光臨寒舍: 我的 GIthub 部落格 學習導圖 一.為什麼要學習記憶體模型與執行緒? 之前我們學習了記憶體模型和執行緒,瞭解了 JMM 和執行緒,初步探究了 JVM 怎麼實現併發,而本篇文章,我們的關注點是 JVM 如何實現高效 併發

執行安全優化——執行安全

文章目錄 一、java中的執行緒安全 1.1 不可變 1.2 絕對執行緒安全 1.3 相對執行緒安全 1.4 執行緒相容 1.5 執行緒對立 二、執行緒安全的實現方法 2.1 互斥同步

執行安全優化——優化

文章目錄 一、自旋鎖與自適應自旋 二、鎖消除 三、鎖粗化 四、輕量級鎖 4.1 輕量級鎖的執行過程 五、偏向鎖 一、自旋鎖與自適應自旋 共享資料的共享時間只有很小一段時間,為此去進行執行緒掛起和

讀書筆記 ---- 《深入理解Java虛擬機器》---- 第12篇:執行安全優化

上一篇:Java記憶體模型與執行緒:https://blog.csdn.net/pcwl1206/article/details/84661639 目  錄: 1  Java語言中的執行緒安全  1.1  不可變  1.2 

java中的執行安全優化

Java的執行緒是對映到作業系統的原生執行緒之上的,如果要阻塞或喚醒一條執行緒,都需要作業系統來幫忙完成,這就需要作業系統來幫忙完成,需要從使用者態轉換到核心態中,狀態轉換需要耗費很多的處理器時間。如果是非常簡單的程式碼同步塊,狀態轉換消耗的時間可能比使用者程式碼執行的時間還要長。 因此可以說,syn

jdk原始碼解析(十二)——執行安全優化

上一節我們說了Java記憶體模型與執行緒、那麼我們這節來了解一下執行緒安全與鎖優化 1 概述 在軟體業發展的初期,程式編寫都是以演算法為核心的,程式設計師會把資料和過程分別作為獨立的部分來考慮,資料代表問題空間中的客體,程式程式碼則用於處理這些資料,這種思維方式直接站在計算機的角度去抽象問題

深入理解java虛擬機器----第十三章執行安全優化

這一部分和java併發程式設計實戰中講的很多東西一樣,所以可以對照著看。 13.1 概述 對於這部分的主題“高效併發”來講,首先需要保證併發的正確性,然後在此基礎上實現高效。本章先從如何保證併發的正確性和如何實現執行緒安全講起。 13.2 執行緒安全 當多個執行緒訪問一個物件時,如果

執行安全優化

摘自《深入理解 Java 虛擬機器:JVM 高階特性與最佳實踐》(第二版)         併發處理的廣泛應用是使得 Amdahl 定律代替摩爾定律成為計算機效能發展源動力的根本原因,也是人類 “壓榨” 計算機運算能力的最有力武器。 概述         在軟體業發展的初期

第13章執行安全優化

執行緒安全 當多個執行緒訪問一個物件的時候,如果不用考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲得正確的結果,那這個物件時執行緒安全的 Java語言中各種操作共享的資料分為以下五類: 不可變:用

《深入理解Java虛擬機器》學習筆記之執行安全優化

二、執行緒安全 定義: “當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲得

Linux執行安全-同步互斥

執行緒安全:多個執行緒執行流對臨界資源的不安全爭搶操作 實現:如何讓執行緒之間安全對臨界資源進行操作就是同步與互斥 互斥:同一時間臨界資源的唯一訪問性 mutex(互斥量) ⼤部分情況,執行緒使⽤的資料都是區域性變數,變數的地址空間線上程棧空間內,這種情況,變數歸屬單

Python中的多執行程式設計,執行安全(一) 聊聊Python中的GIL 聊聊Python中的GIL python基礎之多執行機制 python--threading多執行總結 Python3入門之執行threading常用方法

1. 多執行緒程式設計與執行緒安全相關重要概念 在我的上篇博文 聊聊Python中的GIL 中,我們熟悉了幾個特別重要的概念:GIL,執行緒,程序, 執行緒安全,原子操作。 以下是簡單回顧,詳細介紹請直接看聊聊Python中的GIL  GIL:&n