一、普通同步方法,鎖是當前實例對象html
二、靜態同步方法,鎖是當前類的class對象java
三、同步方法塊,鎖是括號裏面的對象數組
synchronize底層原理:數據結構
Java 虛擬機中的同步(Synchronization)基於進入和退出Monitor對象實現, 不管是顯式同步(有明確的 monitorenter 和 monitorexit 指令,即同步代碼塊)仍是隱式同步都是如此。多線程
同步代碼塊:monitorenter指令插入到同步代碼塊的開始位置,monitorexit指令插入到同步代碼塊的結束位置,JVM須要保證每個monitorenter都有一個monitorexit與之相對應。任何對象都有一個monitor與之相關聯,當且一個monitor被持有以後,他將處於鎖定狀態。線程執行到monitorenter指令時,將會嘗試獲取對象所對應的monitor全部權,即嘗試獲取對象的鎖;app
對象頭:Hotspot虛擬機的對象頭主要包括兩部分數據:Mark Word(標記字段)、Klass Pointer(類型指針)。其中Klass Point是是對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例,Mark Word用於存儲對象自身的運行時數據,它是實現輕量級鎖和偏向鎖的關鍵。性能
Mark Word:用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程 ID、偏向時間戳等等。Java對象頭通常佔有兩個機器碼(在32位虛擬機中,1個機器碼等於4字節,也就是32bit),可是若是對象是數組類型,則須要三個機器碼,由於JVM虛擬機能夠經過Java對象的元數據信息肯定Java對象的大小,可是沒法從數組的元數據來確認數組的大小,因此用一塊來記錄數組長度。優化
Monior:一種同步機制,它叫作內部鎖或者Monitor鎖。Monitor 是線程私有的數據結構spa
每個被鎖住的對象都會和一個monitor關聯(對象頭的MarkWord中的LockWord指向monitor的起始地址),同時monitor中有一個Owner字段存放擁有該鎖的線程的惟一標識,表示該鎖被這個線程佔用操作系統
Java虛擬機對synchronize的優化:
鎖的狀態總共有四種,無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。隨着鎖的競爭,鎖能夠從偏向鎖升級到輕量級鎖,再升級的重量級鎖,可是鎖的升級是單向的,也就是說只能從低到高升級,不會出現鎖的降級,關於重量級鎖,前面咱們已詳細分析過,下面咱們將介紹偏向鎖和輕量級鎖以及JVM的其餘優化手段。
偏向鎖
偏向鎖是Java 6以後加入的新鎖,它是一種針對加鎖操做的優化手段,通過研究發現,在大多數狀況下,鎖不只不存在多線程競爭,並且老是由同一線程屢次得到,所以爲了減小同一線程獲取鎖(會涉及到一些CAS操做,耗時)的代價而引入偏向鎖。偏向鎖的核心思想是,若是一個線程得到了鎖,那麼鎖就進入偏向模式,此時Mark Word 的結構也變爲偏向鎖結構,當這個線程再次請求鎖時,無需再作任何同步操做,即獲取鎖的過程,這樣就省去了大量有關鎖申請的操做,從而也就提供程序的性能。因此,對於沒有鎖競爭的場合,偏向鎖有很好的優化效果,畢竟極有可能連續屢次是同一個線程申請相同的鎖。可是對於鎖競爭比較激烈的場合,偏向鎖就失效了,由於這樣場合極有可能每次申請鎖的線程都是不相同的,所以這種場合下不該該使用偏向鎖,不然會得不償失,須要注意的是,偏向鎖失敗後,並不會當即膨脹爲重量級鎖,而是先升級爲輕量級鎖。
輕量級鎖
假若偏向鎖失敗,虛擬機並不會當即升級爲重量級鎖,它還會嘗試使用一種稱爲輕量級鎖的優化手段(1.6以後加入的),此時Mark Word 的結構也變爲輕量級鎖的結構。輕量級鎖可以提高程序性能的依據是「對絕大部分的鎖,在整個同步週期內都不存在競爭」,注意這是經驗數據。須要瞭解的是,輕量級鎖所適應的場景是線程交替執行同步塊的場合,若是存在同一時間訪問同一鎖的場合,就會致使輕量級鎖膨脹爲重量級鎖。
自旋鎖
輕量級鎖失敗後,虛擬機爲了不線程真實地在操做系統層面掛起,還會進行一項稱爲自旋鎖的優化手段。這是基於在大多數狀況下,線程持有鎖的時間都不會太長,若是直接掛起操做系統層面的線程可能會得不償失,畢竟操做系統實現線程之間的切換時須要從用戶態轉換到核心態,這個狀態之間的轉換須要相對比較長的時間,時間成本相對較高,所以自旋鎖會假設在不久未來,當前的線程能夠得到鎖,所以虛擬機會讓當前想要獲取鎖的線程作幾個空循環(這也是稱爲自旋的緣由),通常不會過久,多是50個循環或100循環,在通過若干次循環後,若是獲得鎖,就順利進入臨界區。若是還不能得到鎖,那就會將線程在操做系統層面掛起,這就是自旋鎖的優化方式,這種方式確實也是能夠提高效率的。最後沒辦法也就只能升級爲重量級鎖了。
鎖消除
消除鎖是虛擬機另一種鎖的優化,這種優化更完全,Java虛擬機在JIT編譯時(能夠簡單理解爲當某段代碼即將第一次被執行時進行編譯,又稱即時編譯),經過對運行上下文的掃描,去除不可能存在共享資源競爭的鎖,經過這種方式消除沒有必要的鎖,能夠節省毫無心義的請求鎖時間,以下StringBuffer的append是一個同步方法,可是在add方法中的StringBuffer屬於一個局部變量,而且不會被其餘線程所使用,所以StringBuffer不可能存在共享資源競爭的情景,JVM會自動將其鎖消除。
synchronize的可重入性:
從互斥鎖的設計上來講,當一個線程試圖操做一個由其餘線程持有的對象鎖的臨界資源時,將會處於阻塞狀態,但當一個線程再次請求本身持有對象鎖的臨界資源時,這種狀況屬於重入鎖,請求將會成功,在java中synchronized是基於原子性的內部鎖機制,是可重入的,所以在一個線程調用synchronized方法的同時在其方法體內部調用該對象另外一個synchronized方法,也就是說一個線程獲得一個對象鎖後再次請求該對象鎖,是容許的,這就是synchronized的可重入性。
本篇參考資料:http://blog.csdn.net/javazejian/article/details/72828483?locationNum=5&fps=1
http://www.cnblogs.com/pureEve/p/6421273.html
http://www.cnblogs.com/paddix/p/5367116.html