在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类的对象吗?我有点困惑。请澄清。
答案 0 :(得分:2)
是的,以上代码将要求同时锁定SomeClass
和xyz
对象。但是,这不会造成任何问题。
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
在同一对象(也称为)上同步。他们正在使用与锁相同的对象!这里的要点是当前线程已经在对象上具有了锁定,因为锁定是基于每个线程的,因此它可以安全地进入同步块,而不会尝试再次获取该锁定。如果确实按每次调用获取了锁,则它将尝试再次获取该锁并被卡住,因为第一个调用已经拥有该锁。