synchronized關鍵字簡介 多線程中篇(十一)

前面說過,Java對象都有與之關聯的一個內部鎖和監視器
內部鎖是一種排它鎖,可以保障原子性、可見性、有序性
從Java語言層面上說,內部鎖使用synchronized關鍵字實現
synchronized能夠修飾方法,靜態方法和實例方法均可以,也能夠修飾一段代碼({} 包裹)
image_5c6cade1_3d6c
synchronized修飾的方法被叫作同步方法
  • 修飾的靜態方法叫作同步靜態方法
  • 修飾的實例方法叫作同步實例方法
  • synchronized修飾的代碼塊(或者一整個方法)就是曾經說過的臨界區
synchronized關鍵字同步機制的使用,須要藉助於鎖對象
synchronized關鍵字修飾靜態方法,鎖對象隱含的是該類的class實例對象;修飾的實例方法隱含的是該對象自己(this)
對於同步代碼段,則須要顯式的指定鎖對象

示例

image_5c6cade1_1bcd
注意:
對於鎖對象,應該聲明爲final的
由於若是一旦鎖對象發生了變化,那麼極可能使用的將不是同一個鎖對象,也就失去了同步的意義了,更甚一步,一般聲明爲private final
如上代碼示例,藉助於synchronized關鍵字,就能夠實現原子性、可見性、有序性,因此對於該臨界區內的代碼,必然不會出現線程安全問題
可是這是一種排他鎖,也就是對臨界區的處理串行化,因此勢必影響性能

鎖泄漏

對於synchronized來講,這是一種內部鎖,對於鎖的申請和釋放,都是藉助於底層實現的,換句話說你只須要使用synchronized關鍵字便可
底層JVM會幫助咱們實現鎖的獲取與鎖的釋放,即便出現問題,也會釋放鎖,因此synchronized的內部鎖不存在鎖泄露問題
對於鎖泄漏,有時候多是同一個線程持續操做,因爲鎖的可重入性,因此並不會發現問題,可是對於高併發,這就極可能爆發出來問題了

調度

Java虛擬機會給每一個內部鎖分配一個入口集 Entry Set,用於記錄等待得到內部鎖的線程
多個線程競爭時,只會有一個線程得到鎖,其餘線程獲取失敗,會進入BLOCKED等待狀態,位於入口集的等待區中
鎖釋放後,會隨機的喚醒一個線程,Java虛擬機內部對於內部鎖是非公平的,也僅僅支持非公平調度,喚醒的線程可能會跟其餘的線程競爭,因此他並不必定能夠競選成功,可能會被再次置入等待狀態
這個過程跟前面介紹的監視器的過程是同樣的

鎖對象的確認

前面提到
synchronized修飾的同步實例方法,鎖對象爲當前對象自己this;靜態方法鎖對象爲該類型對應的xxx.class對象實例;
這都是隱式的,如何確認?其實很簡單
能夠定義另外的方法顯式的聲明鎖對象爲該對象this或者xxx.class對象實例,對其中一個線程進行sleep,觀察顯式方法對鎖的獲取狀況,就能夠佐證這一結論。
若是是不一樣的鎖的話,將不會收到任何影響,若是是同一個鎖就須要進行等待。

同步繼承性

synchronized關鍵字修飾的方法能夠進行同步,對於同步方法的繼承性是什麼樣子的?
好比父類中
public synchronized void service();
子類中
@override
public void service();
對於子類中的方法調用,並不會具備同步的特性,因此,一個方法是否具備同步的特性,在於這個方法自己是否有synchronized修飾

同步代碼塊

synchronized便可以修飾方法,也能夠修飾代碼塊
爲何還要用同步代碼塊?直接加到方法上多省事兒?
synchronized同步保障了原子性、可見性、有序性,這個內部鎖機制是排他的,換言之,至關於部分串行
串行天然能夠解決多線程安全問題,若是整個項目所有都是synchronized的方法,那麼確定不會有線程安全問題,可是爲何不這麼作?還不是由於性能問題,多核CPU放在那裏,難道就只是擺設嘛
既然是至關於串行,很顯然,串行化的代碼越多,那麼效率必然將會越低,因此但願減小非必要的串行化,留給多核機器以及編譯器CPU更多的優化空間
因此同步代碼塊順勢而出
同步代碼塊保障了更少的「串行化」代碼,那麼一個方法中,同步代碼塊以外的代碼是如何進行的?是異步的!
進入同步代碼塊以前會多線程併發,可是一旦執行到同步代碼塊,將會串行

小結

對於synchronized關鍵字,從應用層面上來講是很是簡單的,就只有代碼中的三種樣式,可是底層的原理是很複雜的,涉及到JMM以及原子性、可見性、有序性的概念
因此想要學習synchronized,務必要理解這些概念
對於多線程編程來講,synchronized更大程度上來講,更至關因而一個語法糖,底層的機制所有被封裝了,若是理解了底層的概念,語法糖的東西,就沒什麼理解難度
原子性、可見性、有序性是問題根源,JMM是問題解決方案,編譯器、JVM底層負責實現,synchronized只是一個關鍵字而已,可是synchronized倒是徹底表明了底層的一切
爲何說synchronized關鍵字修飾的方法(代碼段)是線程安全的?那是由於底層的原子性、可見性、有序性的保障。
Java中任何一個對象都有與之關聯的內部鎖和監視器,因此任何的一個對象均可以用來做爲鎖對象
因此,藉助於synchronized關鍵字和鎖對象,進行合理的安排,你必定能夠編寫出來正確的併發程序(自身的安排組織不當怪不得synchronized)
相關文章
相關標籤/搜索