1.synchronized的3種用法 (1)修飾實例方法,作用於當前對象實例加鎖,進入同步代碼前要獲得當前對象實例的鎖(2)修飾靜態方法,作用於當前類對象加鎖,進入同步代碼前要獲得當前類對象的鎖 。也就是給當前類加鎖,會作用於類的所有對象實例,因為靜態成員不屬於任何一個實例對象,是類成員( sta ...
1.synchronized的3種用法
public class Client { public static void main(String[] args) { testSynchronized(); } private static void testSynchronized() { new Foo().sayHello(); } static class Foo { //修飾代碼塊 void sayHello() { synchronized (this) { System.out.println("hello"); } } //修飾示例方法 synchronized void sayGood(){ System.out.println("good"); } //修飾靜態方法 static synchronized void sayHi(){ System.out.println("hi"); } } }
(1)修飾實例方法,作用於當前對象實例加鎖,進入同步代碼前要獲得當前對象實例的鎖
(2)修飾靜態方法,作用於當前類對象加鎖,進入同步代碼前要獲得當前類對象的鎖 。也就是給當前類加鎖,會作
用於類的所有對象實例,因為靜態成員不屬於任何一個實例對象,是類成員( static 表明這是該類的一個靜態
資源,不管new了多少個對象,只有一份,所以對該類的所有對象都加了鎖)。所以如果一個線程A調用一個實
例對象的非靜態 synchronized 方法,而線程B需要調用這個實例對象所屬類的靜態 synchronized 方法,是允
許的,不會發生互斥現象,因為訪問靜態 synchronized 方法占用的鎖是當前類的鎖,而訪問非靜態
synchronized 方法占用的鎖是當前實例對象鎖。
(3)修飾代碼塊,指定加鎖對象,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖。 和 synchronized 方
法一樣,synchronized(this)代碼塊也是鎖定當前對象的。synchronized 關鍵字加到 static 靜態方法和
synchronized(class)代碼塊上都是是給 Class 類上鎖。這裡再提一下:synchronized關鍵字加到非 static 靜態
方法上是給對象實例上鎖。
2.synchronized的底層原理
synchronized 關鍵字底層原理屬於 JVM 層面。通過 JDK 自帶的 javap 命令查看 Foo類的相關位元組碼信息: javap -v Client$Foo.class
synchronized 同步語句塊的實現使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同
步代碼塊的開始位置,monitorexit 指令則指明同步代碼塊的結束位置。 當執行 monitorenter 指令時,線程試圖
獲取鎖也就是獲取 monitor(monitor對象存在於每個Java對象的對象頭中,synchronized 鎖便是通過這種方式獲取
鎖的,也是為什麼Java中任意對象可以作為鎖的原因) 的持有權.當計數器為0則可以成功獲取,獲取後將鎖計數器設
為1也就是加1。相應的在執行 monitorexit 指令後,將鎖計數器設為0,表明鎖被釋放。如果獲取對象鎖失敗,那當
前線程就要阻塞等待,直到鎖被另外一個線程釋放為止。
synchronized 修飾方法的的情況
synchronized 修飾的方法並沒有 monitorenter 指令和 monitorexit 指令,取得代之的確實是
ACC_SYNCHRONIZED 標識,該標識指明瞭該方法是一個同步方法,JVM 通過該 ACC_SYNCHRONIZED 訪問標誌來
辨別一個方法是否聲明為同步方法,從而執行相應的同步調用。