以下代码将成功执行吗?

时间:2018-09-12 17:54:19

标签: java multithreading reentrantlock reentrancy

在Brian Goetz的Java Concurrency in Practice中,有以下示例将Java锁的重入解释为:

public class Widget {
  public synchronized void doSomething() {
    ...
  }
}

public class LoggingWidget extends Widget {
  public synchronized void doSomething() {
    System.out.println(toString() + ": calling doSomething");
    super.doSomething();
  }
}

它说,由于Java中锁的重入,上述代码不会导致死锁,因为锁是基于每个线程而不是基于调用获得的。

但是,如果我们将示例扭曲一下:

public class XYZ {
  public synchronized void someFunction() {
    ...
  }
}

public class SomeClass {
  private XYZ xyz;
  ...
  public synchronized void functionCalled() {
    ...
    xyz.someFunction();
  }
}

我们调用SomeClass的functionCalled(),并且在SomeClass的对象上获得了锁定。现在,将调用someFunction()或换句话说,线程将进入xyz类的someFunction()。 XYZ类的同步功能会要求锁定XYZ类的对象吗?我有点困惑。请澄清。

2 个答案:

答案 0 :(得分:2)

是的,以上代码将要求同时锁定SomeClassxyz对象。但是,这不会造成任何问题。

xyz对象为private,因此任何线程都不可能在调用xyz之前先锁定functionCalled(),因此没有死锁的可能,因为这两个锁始终按SomeClass-> xyz的顺序调用。

答案 1 :(得分:0)

弄清楚它成功与否的最简单方法是简单地运行它。我将您的代码重写为一个有效的示例,并且在两种情况下它的确输出了“ Hello”。

public class XYZ {

  public void someFunction() {
    synchronized (this) {
        System.out.println("Hello");
    }
  }
}

public class SomeClass {
  private XYZ xyz = new XYZ();

  public void functionCalled() {
    synchronized (this) {
      xyz.someFunction();
    }
  }

  public static void main(String[] args) {
    new SomeClass().functionCalled();
  }
}

我还重写了您的方法,改而使用同步块。它们的工作方式是,如果其中有另一个线程并且对象(锁)相同,则没有线程可以进入一个块。在这里,您可以看到两个函数都在this上进行同步,这两个函数引用了两个不同的对象,因为这两个方法属于两个不同的类。这里没有冲突。

functionCalled在SomeClass的实例上同步

someFunction在XYZ实例上同步

这与您的书中的示例不同,我认为示例的要点是锁实际上是相同的!在某些环境中,这可能会导致死锁,但Java中并未指出。举个例子。

public class XYZ {

  public void someFunction() {
    synchronized (this) {
        System.out.println("Hello");
    }
  }
}

public class SomeClass {
  private XYZ xyz = new XYZ();

  public void functionCalled() {
    synchronized (xyz) {
      xyz.someFunction();
    }
  }

  public static void main(String[] args) {
    new SomeClass().functionCalled();
  }
}

它可能看起来很相似,但是在这种情况下,functionCalled在同一对象(也称为)上同步。他们正在使用与锁相同的对象!这里的要点是当前线程已经在对象上具有了锁定,因为锁定是基于每个线程的,因此它可以安全地进入同步块,而不会尝试再次获取该锁定。如果确实按每次调用获取了锁,则它将尝试再次获取该锁并被卡住,因为第一个调用已经拥有该锁。