synchoronized在Jdk1.6以前一直被稱爲重量鎖,性能較差,不多被推薦使用。synchoronized在Jdk1.6中進行了大量優化,性能與JUC中的各類鎖無差,也被逐漸的被大衆所接受。java
synchoronized將鎖狀態記錄在了對象頭的MarkWord中,包含4種鎖狀態:無鎖、偏向鎖、輕量鎖和重量鎖,鎖狀態只能升級不能降級,即 無鎖-->偏向鎖-->輕量鎖-->重量鎖,目的是提升獲取鎖和釋放鎖的效率,對象幾種狀態的獲取和釋放以下:編程
- 偏向鎖
大多數狀況下,鎖不存在多線程競爭,而老是由同一線程得到,爲了讓線程得到鎖的代價更低(消除了同步)而引入了偏向鎖。當對象第一次被線程獲取的時候,會把MarkWord中的偏向標識位設爲01(偏向鎖狀態),同時使用CAS操做將該線程的ID記錄在MarkWord中,若是CAS操做成功,則該線程每次進入到這個鎖相關的同步塊時,虛擬機均可以不進行任何同步操做。
當有另外的線程獲取這個鎖時,偏向模式就結束了,根據鎖對象目前是否處於鎖定的狀態,會恢復到無鎖狀態(01)或者變爲輕量鎖狀態(00),借用《Java併發編程藝術》的一張圖,以下所示:
- 輕量鎖
線程在執行同步塊以前,JVM會先在當前線程棧幀中建立用於存儲鎖記錄的空間,並將對象頭中的MarkWord複製到鎖記錄中,官方稱爲Displaced Mark Word。而後線程嘗試使用CAS操做將對象頭中的MarkWord替換爲指向鎖記錄的指針,若是成功,當前線程得到鎖,若是失敗,表示有其餘線程競爭鎖,當前線程開始自旋,若是自旋完成必定次數(多是50次、100次,能夠經過啓動參數配置自旋次數)當前線程尚未獲取到鎖,那麼鎖會開始膨脹,進化爲重量鎖。
輕量鎖解決時,使用CAS操做將Displace Mark Word 替換會對象頭,若是成功,則表示沒有競爭,若是失敗(已經有線程將鎖的狀態修改成重量鎖),則釋放鎖並喚醒等待線程,借用《Java併發編程藝術》的一張圖,以下所示:
- 重量鎖
由上面的輕量鎖升級而來,一旦升級到重量鎖,那麼其餘線程獲取鎖,都會被阻塞,等待釋放鎖。當持有鎖的線程釋放鎖並喚醒等待線程時,等待的線程就會開始新一輪的競爭。
理解:多線程
- 當單個線程進入同步塊,使用偏向鎖,成本低,不須要使用CAS操做
- 當兩個線程依次進入同步塊,使用輕量鎖,單線程自旋也不會形成太多的CPU損耗
- 當兩個或多個線程進入同步快,使用重量鎖,讓獲取鎖的線程以外的線程進行阻塞等待喚醒,效率更高。
PS:使用openjdk的JOL(Java Object Layout)工具能夠觀察各類線程競爭鎖時,Java對象頭的狀態。併發
參考書籍及網址:工具
PS:研究基於MAC+Idea+JDK1.8 64位性能
Keep Calm and Carry on!優化