Я столкнулся с некоторой проблемой блокировки в моем приложении, которая содержит несколько классов, как показано ниже:
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, может получить доступ к методу 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 и вызывает Client.getInstance () в методе doSomethingThatWillCallClientGetInstanceSeveralTimes () несколько раз.И до завершения метода doSomethingThatWillCallClientGetInstanceSeveralTimes () поток-2 получает блокировку объекта Client.class (как это возможно? Но это произошло) и входит в метод Client.getInstance (поскольку этот метод является статическим синхронизированным методом),По какой-то причине поток-2 не может вернуть «экземпляр» (я думаю, он ожидает, пока Client.class завершит свою инициализацию).В то же время поток-1 не может продолжить работу, потому что ему все еще нужно вызвать Client.getInstance в doSomethingThatWillCallClientGetInstanceSeveralTimes () и не может получить блокировку, поскольку он принадлежит потоку-2.Threaddump сообщает мне, что thread-2 находится в состоянии RUNNABLE, а thread-1 находится в состоянии BLOCKED, ожидая блокировки, принадлежащей thread-2.
Я могу воспроизвести это поведение только в 64-битной Java 6u23 JVM в Windowsи не может воспроизвести его в 32-битной среде Java 6 JVM + Windows.Может кто-нибудь сказать мне, что мне здесь не хватает?Обречен ли этот вид кода вызывать такую блокировку, если да, то как?Мое понимание JLS для этой части неверно?Или это проблема с JVM?Любая помощь приветствуется.Спасибо.