1. 程式人生 > 實用技巧 >synchronized鎖

synchronized鎖

synchronized是一種互斥鎖

  • 一次只能允許一個執行緒進入被鎖住的程式碼塊

synchronized是一種內建鎖/監視器鎖

  • Java中每個物件都有一個內建鎖(監視器,也可以理解成鎖標記),而synchronized就是使用物件的內建鎖(監視器)來將程式碼塊(方法)鎖定的

synchronized鎖作用

  • synchronized保證了執行緒的原子性。(被保護的程式碼塊是一次被執行的,沒有任何執行緒會同時訪問)

  • synchronized還保證了可見性。(當執行完synchronized之後,修改後的變數對其他的執行緒是可見的)

Java中的synchronized,通過使用內建鎖,來實現對變數的同步操作,進而實現了對變數操作的原子性和其他執行緒對變數的可見性

,從而確保了併發情況下的執行緒安全

synchronized底層是是通過monitor物件,物件有自己的物件頭,儲存了很多資訊,其中一個資訊標示是被哪個執行緒持有

synchronized一般我們用來修飾三種東西:

  • 修飾普通方法

  • 修飾程式碼塊

  • 修飾靜態方法

public class SyncDemo {
    // 修飾普通方法,此時用的鎖是SyncDemo物件(內建鎖)
    public synchronized void test1(){}
​
    public void test2(){
    // 修飾程式碼塊,此時用的鎖是SyncDemo物件(內建鎖)-->this
synchronized (this){} } // 修飾靜態方法程式碼塊,靜態方法屬於類方法,它屬於這個類,獲取到的鎖是屬於類的鎖(類的位元組碼檔案物件)-->SyncDemo.class public synchronized static void test3(){} }

類鎖和物件鎖

synchronized修飾靜態方法獲取的是類鎖(類的位元組碼檔案物件),synchronized修飾普通方法或程式碼塊獲取的是物件鎖。

  • 它倆是不衝突的,也就是說:獲取了類鎖的執行緒和獲取了物件鎖的執行緒是不衝突的

重入鎖

public class
Widget { // 鎖住了 public synchronized void doSomething() { ... } } ​ public class LoggingWidget extends Widget { // 鎖住了 public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); //呼叫父類的方法 super.doSomething(); } }

  1. 當執行緒A進入到LoggingWidget的doSomething()方法時,此時拿到了LoggingWidget例項物件的鎖

  2. 隨後在方法上又呼叫了父類Widget的doSomething()方法,它又是被synchronized修飾

  3. 那現在我們LoggingWidget例項物件的鎖還沒有釋放,進入父類Widget的doSomething()方法還需要一把鎖嗎?不需要

因為鎖的持有者是“執行緒”,而不是“呼叫”。執行緒A已經是有了LoggingWidget例項物件的鎖了,當再需要的時候可以繼續“開鎖”進去的!

這就是內建鎖的可重入性

釋放鎖的時機

  1. 當方法(程式碼塊)執行完畢後會自動釋放鎖,不需要做任何的操作。

  2. 當一個執行緒執行的程式碼出現異常時,其所持有的鎖會自動釋放

  • 不會由於異常導致出現死鎖現象