下面的代码会执行成功吗?

Will the following code execute successfully?

在 Brian Goetz 的 Java 并发实践中,有以下示例解释了 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 class 的 someFunction()。 XYZ class 的同步函数会要求锁定 XYZ class 的对象吗?我有点困惑。请说明。

是的,上面的代码将要求锁定 SomeClassxyz 对象。然而,这不会导致任何问题。

xyz对象是private,所以任何线程都不可能先锁xyz再调用functionCalled(),也就没有死机的可能lock 因为这里的两个锁总是按照 SomeClass -> xyz.

的顺序调用

判断它成功与否的最简单方法就是 运行 它。我将您的代码重写为一个工作示例,它实际上在两种情况下都打印出 "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 在同一个对象上同步,也就是。他们使用与锁相同的对象!这里的要点是当前线程已经拥有对象的锁,因为锁是基于每个线程的,所以它可以安全地进入同步块并且不会再次尝试获取锁。如果它确实在每次调用的基础上获得锁,它将尝试再次获得锁并卡住,因为第一次调用已经拥有锁。