天天看點

淺談Java内部鎖synchronized

作者:Running的程式員

了解Java的朋友們都知道jdk提供的用于保證線程安全的鎖有兩類:内部鎖synchronized和顯示鎖Lock,本文對内部鎖synchronized做一些簡要的分析彙總。

内部鎖的使用範式

1.同步執行個體方法

int count;
    synchronized void syncA() {
        count++;
    }           

等效于:

int count;
    void syncA() {
        synchronized (this) {
            count++;
        }
    }           

上述兩個等效的同步執行個體方法都是同步在this目前對象。

2.同步靜态方法(類方法)

public class Foo {
    static int classCount;
    static synchronized void syncB() {
        classCount++;
    }
}           

等效于:

public class Foo {
    static int classCount;
    static void syncB() {
        synchronized (Foo.class) {
            classCount++;
        }
    }
}           

上述兩個同步的類方法都是同步在類對象Foo.class上面,類對象也是對象。

實際中我們也會經常這樣使用:

private final Object lock = new Object();
    
    int count;
    void syncA() {
        synchronized (lock) {
            count++;
        }
    }
           

作為鎖對象(鎖句柄)使用的lock最好要聲明為不可變對象,因為對多個線程來說,隻有同步在相同的鎖(同一把鎖)上才有意義,才能保證共享資料的安全。

内部鎖的特點

  1. 是互斥的,
  2. 是可重入的
  3. 是非公平的

互斥是指鎖一次隻能被一個線程持有:

淺談Java内部鎖synchronized

内部鎖互斥

可重入是指一個線程持有鎖A,那麼它還可以繼續執行被鎖A保護的其它方法(代碼):

public class Foo2 {

    private static final Object lock = new Object();

    static void syncA() {
        synchronized (lock) {
            System.out.println("syncA: do something");
            syncB();
        }
    }

    static void syncB() {
        synchronized (lock) {
            System.out.println("syncB: do something");
        }
    }

    public static void main(String[] args) {
        syncA();
    }

}           

執行main方法可看到如下輸出;

淺談Java内部鎖synchronized

内部鎖是可重入的

内部鎖的可重入是由JVM實作的,在對象頭中會記錄重入的次數,重入時隻需加1即可,無需再次走申請鎖的耗費資源的流程。

非公平是指多個線程在搶占鎖時JVM并不會保證線程先來後到的順序,非公平性可以提升吞吐量,因為少了維護線程順序的開銷.

内部鎖的簡要原理

内部鎖synchronized在JVM中的實作被稱為monitor,即螢幕,是以也叫螢幕鎖。對應的位元組碼指令為:

monitorenter:配置設定鎖

monitorexit:釋放鎖

synchronized對應的位元組碼指令monitorenter和monitorexit總是成對出現(申請到鎖就能釋放鎖),是以你在代碼中使用synchronized無需手動釋放鎖,釋放鎖由JVM保證。如下簡單的代碼

public class Foo {

    private static final Object lock = new Object();
    static int count;

    static void syncA() {
        synchronized (lock) {
            count++;
        }
    }

    public static void main(String[] args) {
        syncA();
        System.out.println(count);
    }
}           

方法syncA的位元組碼指令:

淺談Java内部鎖synchronized

内部鎖的位元組碼指令

官方對monitor的描述:Java中每一個對象都有monitor

淺談Java内部鎖synchronized

内部鎖的monitor模型

這也就回答了為什麼Object類中會有wait/notify/notifyAll等方法

内部鎖的優化和細分類型

内部鎖在代碼層面對應的是synchronized關鍵字,從Java7開始JVM已經開始對synchronized進行優化,并不會像早期實作中直接進入重量級鎖模式。JVM對内部鎖的優化有:

  1. 支援鎖消除,即無鎖(JIT編譯器利用逃逸分析和内聯優化進行運作時的優化處理)
  2. 支援偏向鎖,對象頭中有記錄目前鎖是否是偏向鎖及偏向線程的id
  3. 支援鎖自适應,搶鎖的線程可以自旋也可以直接更新為重量級鎖
淺談Java内部鎖synchronized

内部鎖的優化細分類型

繼續閱讀