Java保证死锁

时间:2019-06-18 15:40:01

标签: java multithreading synchronization synchronized synchronous

我有两节课:

Deadlock1.java

class Client {
   final Object resource1 = "resource1";
   final Object resource2 = "resource2";
   void doS1() {
       synchronized(resource1) {} 
    }
    void doS2() {
       synchronized(resource2) {}
    }
 }

public class Deadlock1 {
  public static void main(String[] args) {
  Client client = new Client();
  new Thread(
      () ->
             {
               client.doS1();
               try {
                Thread.sleep(50);
              } catch (InterruptedException e) {
             }
              client.doS2();
      }).start();

     new Thread(
      () ->
             {
              client.doS2();
              try {
                Thread.sleep(50);
              } catch (InterruptedException e) {
            }
             client.doS1();
      }).start();
  }
}

Deadlock2.java

class Client {
    final Object resource1 = "resource1";
    final Object resource2 = "resource2";  
}

public class Deadlock2{
  public static void main(String[] args) {
     Client client = new Client();

     new Thread(
      () ->
      {
        synchronized (client.resource1) {
        try {
            Thread.sleep(50);
          } catch (InterruptedException e) {
       }

       synchronized (client.resource2) {}
        }
      }).start();

     new Thread(
      () ->
             {
        synchronized (client.resource2) {   
          try {
            Thread.sleep(50);
          } catch (InterruptedException e) {
          }
              synchronized (client.resource1) {}
        }
      }).start();
  }
}

在Deadlock1中没有发生死锁,但在Deadlock2中却发生了。我不明白为什么?而且我不太了解同步块概念的含义。为什么此块是线程代码的一部分,而不是不同线程执行的一些通用代码?

2 个答案:

答案 0 :(得分:2)

同步块阻止在同一监视对象上同时执行代码。在doS1()的情况下,监视对象为resource1,在doS2()中,监视对象为resource2。当线程进入同步块时,它将尝试获取监视对象上的锁。如果它获得了锁,它将继续并仅在它退出该块时释放该锁(或释放该锁)。如果无法获取该锁(因为另一个线程已经拥有该锁,则该线程将阻塞直到释放该锁并可以获取它为止)。

您上面的两个示例Deadlock1Deadlock2没有执行等效的代码。在Deadllock1中,两个监视对象锁不能由同一线程同时获得。

Deadlock2的每个线程中,它试图同时获取两个监视对象的锁。

  1. 线程1获得对resource1的锁定并休眠50毫秒
  2. 与此同时,线程2获得对resource2的锁定并休眠50毫秒
  3. 当线程1继续执行时,它仍然对resource1具有锁定,并尝试获取对resource2的锁定。 resource2锁仍由线程2保持,因此它阻止等待resource2释放。
  4. 与此同时,线程2试图获取对resource1的锁定,但是它仍由线程1保持,因此它阻止等待线程1释放对resource1的监视。
  5. 现在两个线程都被阻塞,等待释放监视器对象,并且由于它们都被阻塞,它们无法释放已锁定的监视器对象,从而导致死锁。

如果我们重写Deadlock1来模仿Deadlock2的功能,使其确实产生死锁,它将看起来像这样:

public class Deadlock1 {
    static class Client {
        final Object resource1 = "resource1";
        final Object resource2 = "resource2";

        void doS1() {
            synchronized (resource1) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                }
                doS2();
            }
        }

        void doS2() {
            synchronized (resource2) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                }
                doS1();
            }
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        new Thread(client::doS1).start();
        new Thread(client::doS2).start();
    }
}

或者,如果我们重写Deadlock2来模仿Deadlock1的功能,以免产生死锁,它将看起来像这样:

public class Deadlock2 {
    static class Client {
        final Object resource1 = "resource1";
        final Object resource2 = "resource2";
    }

    public static void main(String[] args) {
        Client client = new Client();

        new Thread(
                () ->
                {
                    synchronized (client.resource1) {
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                        }

                    }
                    synchronized (client.resource2) {}
                }).start();

        new Thread(
                () ->
                {
                    synchronized (client.resource2) {
                        try {
                            System.out.println("3");
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                        }
                    }
                    synchronized (client.resource1) {}
                }).start();
    }
}

答案 1 :(得分:1)

死锁1:

Thread 1 is acquiring the lock on 'resource1' and immediately releases it.
Thread 1 is waiting for x milliseconds.
Thread 1 is trying to acquire the lock on 'resource2'.

Thread 2 is acquiring the lock on 'resource2' and immediately releases it.
Thread 2 is waiting for x milliseconds.
Thread 2 is trying to acquire the lock on 'resource1'.

由于两个线程永远不会同时拥有相同的资源,所以这不是问题。

死锁2:

Thread 1 is acquiring the lock on 'resource1' and holds it.
Thread 1 is waiting for x milliseconds.
Thread 1 is trying to acquire the lock on 'resource2'.

Thread 2 is acquiring the lock on 'resource2' and and holds it.
Thread 2 is waiting for x milliseconds.
Thread 2 is trying to acquire the lock on 'resource1'.

由于线程1具有resouce1,线程2具有资源2,都无法获得资源,因此导致死锁。

  

我不太理解“概念”的含义   同步块。为什么此块是线程代码的一部分,而不是一部分   不同线程执行的共同代码?

一个同步块描述了代码的一部分,其中资源(对象引用)上的锁被保持。在该块的开头,将获得锁(否则代码将等到这种情况发生)。当块结束时,锁被释放。

具有关键字synchronized的方法的行为相同,只是在对象本身上获得了锁定。

维护这些锁的方法有很多种,您当然可以使用两个线程都在使用的通用共享代码。