一.說一下synchronized關鍵字的底層原理
1.synchronized又叫同步鎖,采用互斥的方式使同一時刻只能有一個線程持有鎖。
2.jdk1.6及以前,synchronized底層是用monitor實現的。monitor是jvm級別的對象,由c++實現。每一個對象對應一個monitor,線程要獲取鎖需要通過一個對象去關聯其monitor,由這個對象來充當鎖介質,因此又叫對象鎖。
3.monitor由三部分組成
(1)owner:關聯當前持有鎖的線程。同一時刻只能有一個線程持有鎖。
(2)EntryList:關聯所有沒有競爭到鎖的線程。當一個線程釋放鎖,會喚醒EntryList中所有線程一起爭搶鎖,與阻塞的先后順序無關,因此是一種非公平鎖。
(3)WaitSet:關聯所有持有鎖后調用wait()進入等待狀態的線程,這些線程進入等待狀態后會釋放鎖,等待被喚醒。
二.線程獲取鎖是如何通過對象關聯monitor的?
1.在HotSpot虛擬機中,對象在內存的存儲可分為三部分:
(1)對象頭Header:Header又分為兩部分
? ? ? ? a.MarkWord:存儲對象自身運行時的數據,如HashCode、分代年齡、鎖標識等。
? ? ? ? b.KlassPoint:存儲指向對象數據類型的指針。
? ? ? ? c.如果對象是一個數組,則還會有第三部分存儲數組的長度。
(2)實例數據InstanceData:存儲對象數據,如成員變量等。
(3)填充數據PaddingData:如果對象頭加實例數據的長度不是8的整數倍,則會用填充數據填到8的整數倍,為無意義數據。
2.Java中每個對象會關聯一個Monitor對象,如果當前線程使用synchronized給對象上鎖,且是重量級鎖,則該對象的MarkWord會存儲指向其Monitor對象的指針,線程通過該指針關聯到其Monitor。
三.Monitor實現的鎖是重量級鎖,說一下鎖升級機制
1.synchronized實現的鎖都是可重入的,鎖的狀態可分為四種:
(1)無鎖
? ? ? ? a.對象未被synchronized上鎖,則是無鎖狀態。
? ? ? ? b.MarkWord存對象的HashCode、分代年齡、偏向鎖標識、鎖狀態。
(2)偏向鎖
? ? ? ? a.很長一段時間內,該鎖只會被一個線程使用,則使用偏向鎖。
? ? ? ? b.對于偏向鎖,線程第一次上鎖會通過CAS操作使對象的MarkWord關聯線程id,后續線程對該鎖的每次重入都只要比較線程id即可,無需再進行CAS操作。即多次重入僅進行一次CAS操作。
? ? ? ? c.MarkWord存鎖關聯的線程id、時間戳epoch、分代年齡、偏向鎖標識、鎖類型標識。
(3)輕量級鎖
? ? ? ? a.如果該鎖被多線程使用,但不同線程的加鎖時間基本是錯開的,即多線程對于鎖的競爭不激烈,則使用輕量級鎖。
? ? ? ? b.對于輕量級鎖,線程第一次上鎖會通過CAS操作使對象的MarkWord關聯線程的鎖記錄路,后續的每次重入也要進行CAS操作,即每次重入都要進行一次CAS操作。
? ? ? ??c.MarkWord存 指向線程棧中鎖記錄的指針、鎖類型標識。
(4)重量級鎖
? ? ? ? a.如果該鎖被多線程競爭使用,則使用重量級鎖。
? ? ? ? b.重量級鎖是通過Monitor實現的,而Monitor是jvm層面的對象,而jvm又是系統級別的,屬于內核態。因此每次操作鎖都要涉及到用戶態和內核態的切換、進程上下文的切換,成本高性能低。
? ? ? ? c.MarkWord關聯 指向Monitor對象的指針、鎖類型標識。
2.鎖升級機制
(1)對象被創建出來,未被synchronized加鎖,此時屬于無鎖狀態。
(2)當線程第一次執行到synchronized代碼塊,給對象加鎖,此時無鎖升級為偏向鎖,意為偏向于第一個加鎖的線程的鎖。若一直只有該線程使用該鎖,則該鎖一直是偏向鎖。
(3)當鎖是偏向鎖時,如果被其他線程訪問,則偏向鎖升級為輕量級鎖。輕量級鎖情況下,未競爭到鎖的線程不會進入阻塞,而是會進入自旋。
自旋:線程不斷重復未成功的操作,直至成功。若一直未成功,則一直重復失敗操作,直至成功為止,因此又稱為自旋鎖。
(4)當輕量級鎖中的某個線程自旋次數達到設置的最大自旋次數時,表明鎖競爭激烈,此時輕量級鎖升級為重量級鎖。
輕量級鎖情況下:若線程未競爭到鎖,不會釋放cpu,而是會自旋,空耗cpu時間片,直到成功競爭到鎖。
重量級鎖情況下:若線程未競爭到鎖,則進入阻塞,釋放cpu。;當鎖被釋放時,由操作系統喚醒該線程。
上述四種狀態隨著鎖競爭的激烈程度不斷升級,構成鎖的升級。鎖的升級是不可逆的,只能升級不能降級。
? ? ?