synchronized鎖
synchronized是一種互斥鎖
-
一次只能允許一個執行緒進入被鎖住的程式碼塊
synchronized是一種內建鎖/監視器鎖
-
Java中每個物件都有一個內建鎖(監視器,也可以理解成鎖標記),而synchronized就是使用物件的內建鎖(監視器)來將程式碼塊(方法)鎖定的
synchronized鎖作用
-
synchronized保證了執行緒的原子性。(被保護的程式碼塊是一次被執行的,沒有任何執行緒會同時訪問)
-
synchronized還保證了可見性
Java中的synchronized,通過使用內建鎖,來實現對變數的同步操作,進而實現了對變數操作的原子性和其他執行緒對變數的可見性
synchronized底層是是通過monitor物件,物件有自己的物件頭,儲存了很多資訊,其中一個資訊標示是被哪個執行緒持有。
synchronized一般我們用來修飾三種東西:
-
修飾普通方法
-
修飾程式碼塊
-
修飾靜態方法
public class SyncDemo { // 修飾普通方法,此時用的鎖是SyncDemo物件(內建鎖) public synchronized void test1(){} public void test2(){ // 修飾程式碼塊,此時用的鎖是SyncDemo物件(內建鎖)-->thissynchronized (this){} } // 修飾靜態方法程式碼塊,靜態方法屬於類方法,它屬於這個類,獲取到的鎖是屬於類的鎖(類的位元組碼檔案物件)-->SyncDemo.class public synchronized static void test3(){} }
類鎖和物件鎖
synchronized修飾靜態方法獲取的是類鎖(類的位元組碼檔案物件),synchronized修飾普通方法或程式碼塊獲取的是物件鎖。
-
它倆是不衝突的,也就是說:獲取了類鎖的執行緒和獲取了物件鎖的執行緒是不衝突的
重入鎖
public classWidget { // 鎖住了 public synchronized void doSomething() { ... } } public class LoggingWidget extends Widget { // 鎖住了 public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); //呼叫父類的方法 super.doSomething(); } }
-
當執行緒A進入到LoggingWidget的
doSomething()
方法時,此時拿到了LoggingWidget例項物件的鎖。 -
隨後在方法上又呼叫了父類Widget的
doSomething()
方法,它又是被synchronized修飾。 -
那現在我們LoggingWidget例項物件的鎖還沒有釋放,進入父類Widget的
doSomething()
方法還需要一把鎖嗎?不需要
因為鎖的持有者是“執行緒”,而不是“呼叫”。執行緒A已經是有了LoggingWidget例項物件的鎖了,當再需要的時候可以繼續“開鎖”進去的!
這就是內建鎖的可重入性
釋放鎖的時機
-
當方法(程式碼塊)執行完畢後會自動釋放鎖,不需要做任何的操作。
-
當一個執行緒執行的程式碼出現異常時,其所持有的鎖會自動釋放。
-
不會由於異常導致出現死鎖現象