我在我的应用程序中遇到了一些锁定问题,其中包含几个类,如下所示:
public interface AppClient {
void hello();
}
public class Client implements AppClient {
public synchronized static AppClient getInstance() {
return instance;
}
public void hello() {
System.out.println("Hello Client");
}
private final static class InnerClient implements AppClient {
public void hello() {
System.out.println("Hello InnerClient");
}
}
private static AppClient instance;
static {
instance = new InnerClient();
doSomethingThatWillCallClientGetInstanceSeveralTimes();
}
}
public class Application {
new Thread() {
AppClient c = Client.getInstance();
c.hello();
}.start();
new Thread() {
AppClient c = Client.getInstance();
c.hello();
}.start();
// ...
new Thread() {
AppClient c = Client.getInstance();
c.hello();
}.start();
}
在doSomethingThatWillCallClientGetInstanceSeveralTimes()方法中,它会做很多涉及很多类的初始化工作,并在初始化期间多次循环调用Client.getInstance静态方法(我明白这不好,但是,这是一个持久的遗留代码库20年).
这是我的问题:
1)我想在类客户端初始化完成之前,只有触发类Client初始化的第一个线程可以访问Client.getInstance方法,因为JVM将在类初始化完成之前在Client.class对象上同步.我阅读了相关主题的JLS并得出了这个结论(第12.4.2节,详细初始化过程,http://java.sun.com/docs/books/jls/third_edition/html/execution.html).
2)但是,这不是我在真实环境中看到的行为.例如,有三个线程调用Client.getInstance(),thread-1触发Client.class初始化,并多次调用doSomethingThatWillCallClientGetInstanceSeveralTimes()方法中的Client.getInstance().在doSomethingThatWillCallClientGetInstanceSeveralTimes()方法完成之前,thread-2获取了Client.class对象的锁(这怎么可能?但它确实发生了),并进入Client.getInstance方法(因为这个方法是一个静态同步方法) .由于某种原因,thread-2无法返回“实例”(我猜它正在等待Client.class完成其初始化).同时,thread-1无法继续,因为它仍然需要在doSomethingThatWillCallClientGetInstanceSeveralTimes()中调用Client.getInstance并且由于它由thread-2拥有而无法获取锁. Threaddump告诉我thread-2处于RUNNABLE状态,而thread-1处于BLOCKED状态,等待thread-2拥有的锁.
我只能在Windows中的64位Java 6u23 JVM中重现此行为,并且无法在32位Java 6 JVM Windows环境中重现它.谁能告诉我在这里我错过了什么?这种代码注定会引起这样的锁定,如果是的话,怎么来的?我对JLS这部分的理解是不正确的?或者它是JVM问题?任何帮助表示赞赏.谢谢.