簡體   English   中英

兩個線程可以同時訪問同步方法嗎?

[英]Can two threads access a synchronized method at the same time?

    public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

當我運行該程序時,我得到的輸出為

阿方斯:加斯頓向我鞠躬! 加斯頓:阿方斯向我鞠躬!

那么,兩個線程可以同時訪問同步方法嗎?

兩個線程可以同時訪問同步方法嗎?

這取決於兩個線程試圖鎖定的對象實例。 兩個線程無法訪問同一對象實例上的相同synchronized方法。 一個將獲得鎖定,另一個將阻塞,直到第一個線程離開該方法。

在您的示例中,實例方法在包含它們的對象上同步。 在這種情況下,當您調用alphonse.bow(...)您將鎖定alphonse對象。 gaston.bow(...)鎖定gaston

您可以通過兩種方法來獲取一個對象的多個實例以鎖定同一對象。

  • 您可以將方法設為staticsynchronized在這種情況下,它們將鎖定類對象本身。 每個類加載器僅這些對象之一。

     public static synchronized void bow(Friend bower) { 
  • 它們都可以鎖定已定義的靜態對象。 就像是:

     private static final Object lockObject = new Object(); ... public void bow(Friend bower) { synchronized (lockObject) { .... } } 
  • 或者,如果您不想將其設為靜態,則可以傳入該對象以進行鎖定。

您的輸出可能類似於以下內容:

  1. gaston線程(可能)首先啟動並調用bow(alphonse)
  2. 這將鎖定gaston物體並輸出: Gaston: Alphonse has bowed to me!
  3. 它調用alphonse.bowBack(this)
  4. 此調用將鎖定alphonse對象並輸出: Alphonse: Gaston has bowed back to me!
  5. alphonse.bowBack(this)退出,解鎖alphonse對象。
  6. gaston.bow(alphonse)退出,解鎖gaston對象。
  7. 然后gaston螺紋退出。
  8. alphonse線程(可能)接下來開始並調用bow(gaston)
  9. 這將鎖定alphonse對象並輸出: Alphonse: Gaston has bowed to me!
  10. 它調用gaston.bowBack(this)
  11. 此調用將鎖定gaston對象並輸出: Gaston: Alphonse has bowed back to me!
  12. gaston.bowBack(this)退出,解鎖gaston對象。
  13. alphonse.bow(gaston)退出,解鎖alphonse對象。

這可能以許多不同的順序發生。 即使稍后調用start()方法, alphonse線程也可以首先運行。 如果alphonse.bowBack(...)當前正在運行,那么鎖alphonse.bow(...)您免於遭受的唯一alphonse.bow(...)是調用alphonse.bow(...) 正如@ user988052指出的那樣,由於每個線程都鎖定自己的對象,然后嘗試鎖定另一個對象,因此很容易會出現死鎖。

那么,兩個線程可以同時訪問同步方法嗎?

是和否:

  • 是的,如果在類的不同實例上調用了該方法。

  • 不,兩個線程不能同時在類的同一實例上調用同步方法。 即使兩個線程調用不同的方法也是如此(只要實例相同)。

我沒有詳細檢查您的代碼,但我想我知道有關如何創建死鎖典型示例。

但是,您不應僅一次調用它來嘗試創建死鎖。

循環創建線程,極有可能導致死鎖:

for ( int i = 0; i < 1000; i++ ) {
    final Friend alphonse =
        new Friend("Alphonse");
    final Friend gaston =
        new Friend("Gaston");
    new Thread(new Runnable() {
        public void run() { alphonse.bow(gaston); }
    }).start();
    new Thread(new Runnable() {
        public void run() { gaston.bow(alphonse); }
    }).start();
}

請注意,您不會死鎖2000個線程:只有其中一些會死鎖。 您可以通過對程序/ JVM進行線程驗證來驗證這一點。

使用synced關鍵字,您可以鎖定實例方法的實例或靜態方法的類。 因此,在這里您可以保證最多有一個線程在給定的時間在給定的實例上執行bow或bowBack(如果一個線程在執行bow,則其他線程都不能執行bowBack,因為這兩個方法都在同一鎖上同步)...

還有一個評論:由於鎖是可重入的,一旦線程獲得了鎖,它就可以輸入在同一鎖上同步的其他方法。

如《 死鎖教程》中 所述,此代碼通常來自此代碼。

死鎖運行時,兩個線程極有可能在嘗試調用bowBack時阻塞。 兩個塊都不會結束,因為每個線程都在等待另一個退出弓。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM