为什么 ExecutorService 在从 class 加载程序线程启动时不处理提交的任务?
Why does ExecutorService not process submitted tasks when started from class loader thread?
我有一个 class 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();
}
此构造从未 returns(预计在 1 秒和一点后 return)来自 ExecutorService.awaitTermination
。
如果我通过注释(并注释掉另一个)切换到标记为 "working alternative" 的代码并注释掉 Singleton
中的 static
块,代码将按预期执行(method1
和 method2
被调用,return 根据输出以相反的顺序调用。
由于 "working alternative" 阻止了这个问题,我可以忍受它,我正在寻找对此行为的解释。
我打算使用 Class.forName
而不是 Singleton.init
是为了能够向 Singleton
添加更多静态初始化任务,而无需考虑它们是否包含在预编译器中- 初始化例程。我同意整个设置并不理想。
你走错了方向。首先,不要在 class 初始化时进行大量计算。在 class 初始化完成之前,class 方法的调用受到限制。这个想法是不向 class 方法显示尚未初始化的变量。只能执行直接从静态初始化器调用的方法,否则会被阻塞。在您的情况下,并行任务对 method1
和 method2
的调用被阻止。
通常,尽可能避免使用静态变量。而是创建对象的实例。对于给定的情况,创建一个 class Singleton 的实例,所有变量都从静态字段转换为实例字段。
最后,不要运行一个线程只调用
thread.start();
thread.join();
最好直接调用传入线程的方法为Runnable
。
我有一个 class 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();
}
此构造从未 returns(预计在 1 秒和一点后 return)来自 ExecutorService.awaitTermination
。
如果我通过注释(并注释掉另一个)切换到标记为 "working alternative" 的代码并注释掉 Singleton
中的 static
块,代码将按预期执行(method1
和 method2
被调用,return 根据输出以相反的顺序调用。
由于 "working alternative" 阻止了这个问题,我可以忍受它,我正在寻找对此行为的解释。
我打算使用 Class.forName
而不是 Singleton.init
是为了能够向 Singleton
添加更多静态初始化任务,而无需考虑它们是否包含在预编译器中- 初始化例程。我同意整个设置并不理想。
你走错了方向。首先,不要在 class 初始化时进行大量计算。在 class 初始化完成之前,class 方法的调用受到限制。这个想法是不向 class 方法显示尚未初始化的变量。只能执行直接从静态初始化器调用的方法,否则会被阻塞。在您的情况下,并行任务对 method1
和 method2
的调用被阻止。
通常,尽可能避免使用静态变量。而是创建对象的实例。对于给定的情况,创建一个 class Singleton 的实例,所有变量都从静态字段转换为实例字段。
最后,不要运行一个线程只调用
thread.start();
thread.join();
最好直接调用传入线程的方法为Runnable
。