深入理解java:2.2. 同步鎖Synchronized及其實現原理


同步的基本思想

為了保證共享數據在同一時刻只被一個線程使用,我們有一種很簡單的實現思想,就是 

在共享數據里保存一個鎖 ,當沒有線程訪問時,鎖是空的。

當有第一個線程訪問時,就 在鎖里保存這個線程的標識 並允許這個線程訪問共享數據。

在當前線程釋放共享數據之前,如果再有其他線程想要訪問共享數據,就要 等待鎖釋放 。

 

  • 在共享數據里保存一個鎖
  • 在鎖里保存這個線程的標識
  • 其他線程訪問已加鎖共享數據要等待鎖釋放

 

 

Jvm同步的實現

jvm中有以下三種鎖(由上到下越來越“重量級”):

  1. 偏向鎖
  2. 輕量級鎖
  3. 重量級鎖

 

 

 

重量級鎖

Synchronized 原理

我們直接參考JVM規范中描述:每個對象有一個監視器鎖(monitor)

 

當monitor被占用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:

 

1、如果monitor的進入數為0,則該線程進入monitor,然后將進入數設置為1,該線程即為monitor的所有者。

 

2、如果線程已經占有該monitor,只是重新進入,則進入monitor的進入數加1.

 

3.如果其他線程已經占用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。

 

 

 

Synchronized的語義底層是通過一個monitor的對象來完成,其實wait/notify等方法也依賴於monitor對象,

 

這就是為什么只有在同步的塊或者方法中才能調用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。

 

 

Synchronized是通過對象內部的一個叫做監視器鎖(monitor)來實現的。

但是監視器鎖本質又是依賴於底層的操作系統的互斥鎖(Mutex Lock)來實現的。而操作系統實現線程之間的切換這就需要從用戶態轉換到核心態,這個成本非常高,狀態之間的轉換需要相對比較長的時間,這就是為什么Synchronized效率低的原因。

因此,這種依賴於操作系統互斥鎖(Mutex Lock)所實現的鎖我們稱之為“重量級鎖”。

 

輕量級鎖 

  鎖的狀態總共有四種:無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。

   JDK 1.6中默認是開啟偏向鎖和輕量級鎖的,我們也可以通過-XX:-UseBiasedLocking來禁用偏向鎖。

 

輕量級鎖的核心思想就是“被加鎖的代碼不會發生並發,如果發生並發,那就膨脹成重量級鎖(膨脹指的鎖的重量級上升,一旦升級,就不會降級了)”。

   輕量級鎖依賴了一種叫做CAS(compare and swap)的操作。

 

術語定義

術語 英文 說明
CAS Compare and Swap

比較並設置。

用於在硬件層面上提供原子性操作

在 Intel 處理器中,比較並交換通過指令cmpxchg實現。比較是否和給定的數值一致,如果一致則修改,不一致則不修改。

偏向鎖

根據輕量級鎖的實現,我們知道雖然輕量級鎖不支持“並發”,遇到“並發”就要膨脹為重量級鎖,但是輕量級鎖可以支持多個線程以串行的方式訪問同一個加鎖對象。

比如A線程可以先獲取對象o的輕量鎖,然后A釋放了輕量鎖,這個時候B線程來獲取o的輕量鎖,是可以成功獲取得,以這種方式可以一直串行下去。

 

之所以能實現這種串行,是因為有一個釋放鎖的動作。那么假設有一個加鎖的java方法,這個方法在運行的時候其實從始至終只有一個線程在調用,但是每次調用完卻也要釋放鎖,下次調用還要重新獲得鎖。

那么我們能不能做一個假設:“假設加鎖的代碼從始至終就只有一個線程在調用,如果發現有多於一個線程調用,再膨脹成輕量級鎖也不遲”。這個假設,就是偏向鎖的核心思想。

   偏向鎖依賴了一種叫做CAS(compare and swap)的操作。

 

總結 

  本文重點介紹了JDk中采用輕量級鎖和偏向鎖等對Synchronized的優化,

   但是這兩種鎖也不是完全沒缺點的,比如競爭比較激烈的時候,不但無法提升效率,反而會降低效率,因為多了一個鎖升級的過程,這個時候就需要通過-XX:-UseBiasedLocking來禁用偏向鎖。下面是這幾種鎖的對比:

優點

缺點

適用場景

偏向鎖

加鎖和解鎖不需要額外的消耗,和執行非同步方法比僅存在納秒級的差距。

如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗

適用於只有一個線程訪問同步塊場景。

輕量級鎖

競爭的線程不會阻塞,提高了程序的響應速度。

如果始終得不到鎖競爭的線程使用自旋會消耗CPU

追求響應時間

同步塊執行速度非常快。

重量級鎖

線程競爭不使用自旋,不會消耗CPU。

線程阻塞,響應時間緩慢。

追求吞吐量

同步塊執行速度較長。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM