簡體   English   中英

為什么這個程序中的同一個對象沒有死鎖 - Java 多線程

[英]Why no deadlock on the same object in this program - Java Multithreading

我有以下三節課。 Main,兩個線程和方法同步的Obj

public class DeadLocks {

    public static void main(String[] args) {
        SyncObj so = new SyncObj();
        ThreadObj to = new ThreadObj(so);
        ThreadObj1 to1 = new ThreadObj1(so);

        to.start();
        to1.start();
    }
}

class SyncObj {
    synchronized void foo() {
        System.out.println("Foo Started");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bar();
    }

    synchronized void bar() {
        System.out.println("bar started");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        foo();
    }

}

class ThreadObj1 extends Thread {
    SyncObj so;

    public ThreadObj1(SyncObj so) {
        this.so = so;
    }

    @Override
    public void run() {
        so.bar();
    }
}

class ThreadObj extends Thread {
    SyncObj so;

    public ThreadObj(SyncObj so) {
        this.so = so;
    }

    @Override
    public void run() {
        so.foo();
    }
}

在上面的代碼中,我在同一個對象上調用了同步方法。 兩個方法同時執行和調用,不存在死鎖情況。 誰能解釋一下為什么? 抱歉問了這么愚蠢的問題。

據我所知,您在這兩種情況下都使用相同的對象( so )。 所以沒有死鎖的情況。 您需要鎖定兩個或多個對象,其中每個臨界區都需要與它持有的鎖不同的鎖。 另一個鎖由“另一個”線程持有。

令人困惑,這將啟發:“ https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html

在您描述的場景中,您never ever deadlock 在這種情況下,只有一個so正被共享,並且正在與同步。

我舉個例子來說明一下:

假設安迪和桑迪正在玩兩個足球B1B2

現在進一步假設 Andy 和 Sandy 分別擁有球B1B2 例如,安迪有球B1 ,桑迪有球B2

現在他們設計了一個游戲,其中每個人都需要兩個球。 現在 Sandy 也想要B1球,同時,Andy 想要B2 球

並且他們兩個都不能放棄他們持有的球。 安迪不會放棄B1 ,反之亦然。

So both of them cannot continue and are stuck. We call this a deadlocked situation.

希望這個例子有幫助。 您可以發揮您的想象力將比賽中的球數增加到 3 或 4 個(依此類推……)和/或增加球員人數。

您不會遇到死鎖,因為您的程序不滿足形成它的四個必要條件中的兩個:

  • 互斥- 是的,
  • 保持部分分配- 不,
  • 沒有先發制人- 是的,
  • 循環依賴- 沒有

您至少需要兩個資源“A”和“B”才能形成死鎖。 一個線程應該抓取“A”並嘗試抓取“B”,而另一個線程應該抓取“B”並嘗試抓取“A”。

我不知道你為什么期望這里會出現僵局。 確實只有一個對象可以訪問同步部分,但是它可以隨心所欲地訪問。 您可以修改代碼以使其清楚:

public class DeadLocks {

     public static void main(String[] args) {
         SyncObj so = new SyncObj();
         ThreadObj to = new ThreadObj(so);
         ThreadObj1 to1 = new ThreadObj1(so);

         to.start();
         to1.start();
     }
 }

 class SyncObj {
     synchronized void foo(String msg) {
         System.out.println("Foo Started: " + msg);
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         bar(msg);
     }

     synchronized void bar(String msg) {
         System.out.println("bar started: " + msg);
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         foo(msg);
     }

 }

 class ThreadObj1 extends Thread {
     SyncObj so;

     public ThreadObj1(SyncObj so) {
         this.so = so;
     }

     @Override
     public void run() {
         so.bar("TO1");
     }
 }

 class ThreadObj extends Thread {
     SyncObj so;

     public ThreadObj(SyncObj so) {
         this.so = so;
     }

     @Override
     public void run() {
         so.foo("TO");
     }
 }

您可以看到以下輸出:

Foo Started: TO
bar started: TO
Foo Started: TO
bar started: TO
Foo Started: TO
bar started: TO

您可以將 'synchronized' 視為 lock(this),其中 'this' 是一個 SyncObj 實例。 因此只有一個鎖,不可能獲得死鎖。

雖然其他人已經指出只有當你有兩個資源時才會發生死鎖,其中每個線程鎖定一個然后等待另一個,但我認為缺少一個關鍵點,你的整個困惑可能來自:

方法上的synchronize不會為該特定方法創建鎖,而是為其所屬的整個對象創建鎖。 因此,您的課程相當於:

class SyncObj {
    void foo() {
        synchronized(this) {
            System.out.println("Foo Started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bar();
        }
    }

    void bar() {
        synchronized(this) {
          System.out.println("bar started");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          foo();
        }
    }
}

現在應該更清楚了,為什么你沒有陷入僵局。

您可以通過引入兩種資源(每個方法一個)來輕松修改代碼,使其容易出現死鎖:

class SyncObj {
    private Object foolock = new Object();
    private Object barlock = new Object();

    void foo() {
        synchronized(foolock) {
            System.out.println("Foo Started");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bar();
        }
    }

    void bar() {
        synchronized(barlock) {
          System.out.println("bar started");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          foo();
        }
    }
}

使用單個實例不可能發生死鎖,因為同一個對象的兩個線程不能訪問多個同步方法。 在上面的例子中,如果線程 1 正在訪問 foo 方法。 線程 2 不能訪問 foo 或 bar 方法。 直到線程 1 完成它的任務

暫無
暫無

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

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