中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解Java Synchronized的使用及底層原理

發布時間:2021-09-24 17:29:24 來源:億速云 閱讀:120 作者:柒染 欄目:開發技術

如何理解Java Synchronized的使用及底層原理,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

一、線程安全問題

1、臨界資源

多線程編程中,有可能會出現多個線程同時訪問同一個共享、可變資源的情況,這個資源我們稱之其為臨界資源;這種資源可能是:對象、變量、文件等。

  1. 共享:資源可以由多個線程同時訪問

  2. 可變:資源可以在其生命周期內被修改

2、線程安全問題

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

3、如何解決線程安全問題

互斥同步(Mutual Exclusion & Synchronization)是一種最常見也是最主要的并發正確性保障手段。同步是指在多個線程并發訪問共享數據時,保證共享數據在同一個時刻只被一條(或者是一些,當使用信號量的時候)線程使用。而互斥是實現同步的一種手段,臨界區(Critical Section)、互斥量(Mutex)和信號量(Semaphore)都是常見的互斥實現方式。

在Java里面,最基本的互斥同步手段就是synchronized關鍵字,另外還有從JDK1.5開始引入了JUC里面的Lock接口,其中用的比較多的就是ReentrantLock,后面也會進行介紹。

二、synchronized使用介紹

synchronized是JVM內置的,是可重入的,其使用方法有三種:加在static修飾的靜態方法上,加在普通方法上,同步代碼塊三種方式。

  1. 加在靜態方法上(public synchronized static void test()),鎖的是當前類的Class對象

  2. 加在實例方法上(public synchronized void test()),鎖的是當前對象

  3. synchronized同步代碼塊(synchronized(object) {......}),鎖的是synchronized后面括號里面的對象

從上面可以看出synchronized鎖的其實都是對象。

三、synchronized實現原理

1、synchronized底層指令:monitorenter和monitorexit

synchronized是基于JVM內置鎖實現,通過內部對象Object Monitor(監視器鎖)實現,基于進入與退出Monitor對象實現方法與代碼塊同步,監視器鎖的實現依賴底層操作系統的Mutex lock(互斥鎖)實現,它是一個重量級鎖性能較低。當然,JVM內置鎖在1.5之后版本做了重大的優化,如鎖粗化(Lock Coarsening)、鎖消除(Lock Elimination)、輕量級鎖(Lightweight Locking)、偏向鎖(Biased Locking)、適應性自旋(Adaptive Spinning)等技術來減少鎖操作的開銷,內置鎖的并發性能已經基本與Lock持平。

注意:只有synchronized鎖升級為重量級鎖時才會用到Object Monitor(監視器鎖)。

synchronized關鍵字被編譯成字節碼后會被翻譯成monitorenter 和monitorexit 兩條指令分別在同步塊邏輯代碼的起始位置與結束位置。

public class TestSynchronized {
    private Object obj = new Object();
    public void testLock() {
        synchronized (obj) {
            System.out.println("獲取了鎖");
        }
    }
}

我們通過javap -c TestSynchronized.class將上面代碼的class文件進行反匯編,可以看到如下所示:我們看到了monitorenter 和monitorexit 兩條指令,但是monitorexit卻出現了兩次,原因如下:

  • 第一個monitorexit指令是同步代碼塊正常釋放鎖的一個標志;

  • 如果同步代碼塊中出現Exception或者Error,則會調用第二個monitorexit指令來保證釋放鎖

public void testLock();
    Code:
       0: aload_0
       1: getfield      #3                  // Field obj:Ljava/lang/Object;
       4: dup
       5: astore_1
       6: monitorenter
       7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: ldc           #5                  // String 鑾峰彇浜嗛攣
      12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: aload_1
      16: monitorexit
      17: goto          25
      20: astore_2
      21: aload_1
      22: monitorexit
      23: aload_2
      24: athrow
      25: return
    Exception table:
       from    to  target type
           7    17    20   any
          20    23    20   any

2、Object Monitor(監視器鎖)機制

上面提到了,只有synchronized鎖升級為重量級鎖時才會用到Object Monitor(監視器鎖)。我們看一下Object Monitor的實現機制是什么?查看OpenJDK源碼可以看到Object Monitor由C++語言實現,打開JDK源碼目錄 “jdk\hotspot\src\share\vm\runtime“可以看到objectMonitor.hpp,這個就是監視器鎖的實現,截取一段代碼如下:

ObjectMonitor() {
	_header       = NULL; //對象頭
	_count        = 0;	//記錄加鎖次數,鎖重入時用到
	_waiters      = 0, //當前有多少處于wait狀態的thread
	_recursions   = 0; //記錄鎖的重入次數
	_object       = NULL;
	_owner        = 0; //指向持有ObjectMonitor對象的線程
	_WaitSet      = NULL; //處于wait狀態的線程,會被加入到_WaitSet
	_WaitSetLock  = 0 ;
	_Responsible  = NULL ;
	_succ         = NULL ;
	_cxq          = NULL ;
	FreeNext      = NULL ;
	_EntryList    = NULL ;//處于等待加鎖block狀態的線程,會被加入到該列表
	_SpinFreq     = 0 ;
	_SpinClock    = 0 ;
	OwnerIsThread = 0 ;
	_previous_owner_tid = 0;
}

其中幾個比較重要的字段:

  • _header 對象頭,前面說過synchronized鎖升級為重量級鎖之后才會用到objectMonitor,這時候對象頭的Mark word會有一個指向重量級鎖Monitor的指針

  •  _count 線程獲取鎖的次數,每加鎖一次該值加1。

  • _waiters 當前有多少處于wait狀態的thread

  • _recursions 鎖的重入次數 

  • _owner 指向持有ObjectMonitor對象的線程地址。 

  • _WaitSet 存放調用wait方法,而進入等待狀態的線程的隊列。

  • _EntryList 處于等待加鎖block狀態的線程,會被加入到該列表

ObjectMonitor的加鎖解鎖過程如下圖所示,ObjectMonitor中有兩個隊列,_WaitSet 和 _EntryList,用來保存ObjectWaiter對象列表(每個等待鎖的線程都會被封裝成ObjectWaiter對象);整個monitor運行的機制過程如下:

  • _owner指向持有ObjectMonitor對象的線程,當多個線程同時訪問一段同步代碼時,首先會進入 _EntryList 集合

  • 當線程獲取到對象的monitor 后進入 _Owner 區域并把monitor中的owner變量設置為當前線程的同時,monitor中的計數器count加1,

  • 若已經獲取鎖的線程調用 wait() 方法,將釋放當前持有的monitor,owner變量恢復為null,count自減1,同時該線程進入 WaitSet集合中等待被喚醒。

  • 若當前線程執行完畢也將釋放monitor(鎖)并復位變量的值,以便其他線程進入獲取monitor(鎖)。

如何理解Java Synchronized的使用及底層原理

關于如何理解Java Synchronized的使用及底層原理問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

合阳县| 抚远县| 司法| 阿瓦提县| 和政县| 甘谷县| 左云县| 抚宁县| 邹平县| 海盐县| 阿鲁科尔沁旗| 肇源县| 安泽县| 巴彦县| 三江| 洪江市| 如东县| 镇安县| 亳州市| 平乡县| 宾阳县| 乌拉特后旗| 贵南县| 建德市| 分宜县| 波密县| 景泰县| 依兰县| 丹东市| 山西省| 廊坊市| 深泽县| 澄城县| 德格县| 革吉县| 重庆市| 新密市| 漳平市| 通州区| 华容县| 密山市|