synchronized 作為悲觀鎖,鎖住了什麼?


synchronized 作為悲觀鎖,鎖住了什麼?


繼續來認識 「synchronized」,上篇文章 我們瞭解了 「synchronized」 是在多線程併發競爭同一資源的時候使用,這一篇我們來了解,synchronized 作為悲觀鎖,鎖住了什麼?

鎖實例對象

上篇文章我們就有鎖實例對象的代碼樣例,只是當時沒有細說這個概念。我們再寫一個代碼來測試一下。代碼邏輯是這樣的:我們寫 2 個 「synchronized」 實例方法,讓 5 個線程隨機執行 2 個方法。代碼如下:

只有一個實例對象的情況:

<code>public class SynchronizedTest {

    public static void main(String[] args) {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        for (int i = 0; i             int thisi = i;
            Thread thread = new Thread(() -> {
                if (thisi % 2 == 0) {
                    synchronizedTest.testSynchronizedMethod1();
                } else {
                    synchronizedTest.testSynchronizedMethod2();
                }
            });

            thread.start();
        }
    }

    public synchronized void testSynchronizedMethod1() {
        System.out.printf("%s-testSynchronizedMethod1-start-count=%s\\n", Thread.currentThread().getName(), count);
        count ++;
        System.out.printf("%s-testSynchronizedMethod1-end-count=%s\\n", Thread.currentThread().getName(), count);
    }

    public synchronized void testSynchronizedMethod2() {
        System.out.printf("%s-testSynchronizedMethod2-start-count=%s\\n", Thread.currentThread().getName(), count);
        count ++;
        System.out.printf("%s-testSynchronizedMethod2-end-count=%s\\n", Thread.currentThread().getName(), count);
    }
}/<code>

運行結果:

synchronized 作為悲觀鎖,鎖住了什麼?

這份代碼裡面有 5 個線程競爭一個 synchronizedTest 資源,所以只能串行跑,我們這裡用了 2 個方法,為了讓大家更清楚的明白鎖的是對象,而不是鎖對象裡面的某個方法。如果是鎖方法,那麼線程「Thread-0」調用testSynchronizedMethod1方法和線程「Thread-1」調用testSynchronizedMethod2方法就不會串行執行,會併發執行;但是結果是串行執行,也就驗證了是鎖 synchronizedTest 對象而不是方法。通過 count 結果,更加清晰的瞭解,方法是串行執行的。

每個線程一個實例對象的情況:

<code>public class SynchronizedTest {

    public static void main(String[] args) {
        for (int i = 0; i             SynchronizedTest synchronizedTest = new SynchronizedTest();
            int thisi = i;
            Thread thread = new Thread(() -> {
                if (thisi % 2 == 0) {
                    synchronizedTest.testSynchronizedMethod1();
                } else {
                    synchronizedTest.testSynchronizedMethod2();
                }
            });
            thread.start();
        }
    }

    public synchronized void testSynchronizedMethod1() {
        System.out.printf("%s-testSynchronizedMethod1-start-count=%s\\n", Thread.currentThread().getName(), count);
        count ++;
        System.out.printf("%s-testSynchronizedMethod1-end-count=%s\\n", Thread.currentThread().getName(), count);
    }

    public synchronized void testSynchronizedMethod2() {
        System.out.printf("%s-testSynchronizedMethod2-start-count=%s\\n", Thread.currentThread().getName(), count);
        count ++;
        System.out.printf("%s-testSynchronizedMethod2-end-count=%s\\n", Thread.currentThread().getName(), count);
    }
}/<code>
synchronized 作為悲觀鎖,鎖住了什麼?

這份代碼是每個線程有獨立的對象,線程之間沒有競爭,所以互不影響,count 也都是線程獨立的,所以 end 結果都是 1。這個例子為了和下面的鎖類的 Class 對象做對比,先記住鎖實例對象的情況,只要線程之間鎖的不是同一個實例對象,線程之間就沒有競爭。

鎖類的 Class 對象

我們將本來實例方法改成 static 靜態方法,這份代碼 IDE 會提示異常,咱先忽略異常,可以執行成功。

<code>public class SynchronizedTest {

    public static void main(String[] args) {
        for (int i = 0; i             SynchronizedTest synchronizedTest = new SynchronizedTest();
            Thread thread = new Thread(() -> {
                synchronizedTest.testSynchronizedStaticMethod();
            });
            thread.start();
        }
    }
    
    public static synchronized void testSynchronizedStaticMethod() {
        System.out.println("testSynchronizedStaticMethod-start-" + Thread.currentThread().getName());
        System.out.println("testSynchronizedStaticMethod-end-" + Thread.currentThread().getName());
    }
}/<code>

代碼結果:

synchronized 作為悲觀鎖,鎖住了什麼?

我們可以看出,5 個線程執行到testSynchronizedStaticMethod方法都是串行執行,這和上面的例子有點差別,上面例子 5 個實例對象的情況下是互不影響的。所以可以看出,加上 static 不是鎖實例對象,而是「鎖 Class 對象」

總結

這一篇我們講了 「synchronized」 修飾方法時的 2 種鎖機制:鎖實例對象和鎖類的 Class 對象。從鎖的「粗粒度」來對比,鎖類 Class 對象的粒度大於鎖實例對象。

同步方法是 「synchronized」 最簡單的用法,接下來一篇會講 「synchronized」 的代碼塊用法,代碼塊用法把鎖粒度再降一個檔次,期待你到時閱讀。

歡迎關注 LieBrother,一起學習進步。



分享到:


相關文章: