У меня есть класс Singleton
(упрощенно для этого примера)
public class Singleton {
private final static Lock METHOD_1_LOCK = new ReentrantLock();
private final static Lock METHOD_2_LOCK = new ReentrantLock();
static {
try {
init();
}catch(InterruptedException ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static void init() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(() -> {
method1();
});
executorService.submit(() -> {
method2();
});
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
public static List<String> method1() {
METHOD_1_LOCK.lock();
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1");
return Stream.of("b").collect(Collectors.toList());
}finally {
METHOD_1_LOCK.unlock();
}
}
public static List<String> method2() {
METHOD_2_LOCK.lock();
try {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2");
return Stream.of("a").collect(Collectors.toList());
}finally {
METHOD_2_LOCK.unlock();
}
}
private Singleton() {
}
}
, который я хотел предварительно инициализировать, вызвав Class.forName
в отдельном потоке:
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Class.forName(Singleton.class.getName());
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
// working alternative:
// try {
// Singleton.init();
// }catch(InterruptedException ex) {
// ex.printStackTrace();
// }
});
thread.start();
thread.join();
}
Эта конструкция никогда не вернется (ожидается, что вернется через 1 секунду и немного) из ExecutorService.awaitTermination
.
Если я переключаюсь на код, помеченный как «рабочая альтернатива», комментируя его (и комментируя другой), и комментируя блок static
в Singleton
, код выполняется должным образом (method1
и method2
вызываются и возвращаются в обратном порядке согласно выводу).
Поскольку «рабочая альтернатива» предотвращает проблему, и я могу с ней жить, я ищу объяснение этому поведению.
Мое намерение использовать Class.forName
вместо Singleton.init
состояло в том, чтобы иметь возможность добавлять больше статических задач инициализации к Singleton
без необходимости думать, охватываются ли они подпрограммой предварительной инициализации. Я согласен, что вся эта установка не идеальна.