java - 为什么 ExecutorService 从类加载器线程启动时不处理提交的任务?
问题描述
我有一个类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();
}
这个构造永远不会从ExecutorService.awaitTermination
.
如果我通过注释(并注释掉另一个)切换到标记为“工作替代”的代码并注释掉代码中的static
块,则按Singleton
预期执行(method1
并method2
根据输出以相反的顺序调用和返回)。
由于“工作替代方案”可以防止问题并且我可以忍受它,我正在寻找这种行为的解释。
我的意图是使用Class.forName
而不是Singleton.init
能够添加更多静态初始化任务,Singleton
而无需考虑它们是否被预初始化例程覆盖。我同意整个设置并不理想。
解决方案
你正朝着错误的方向前进。首先,不要在类初始化时进行繁重的计算。在类初始化完成之前,类方法的调用受到限制。这个想法不是向类方法显示尚未初始化的变量。只有直接从静态初始化器调用的方法才能被执行,否则它们会被阻塞。在您的情况下,并行任务的调用method1
和method2
来自并行任务的调用被阻止。
通常,尽可能避免使用静态变量。而是创建对象的实例。对于给定的情况,创建一个类 Singleton 的实例,其中所有变量都从静态字段转换为实例字段。
最后,不要运行线程只是为了调用
thread.start();
thread.join();
最好直接调用传递给线程的方法作为Runnable
.
推荐阅读
- perl - Perl - 在 cp1251 中复制到剪贴板
- mysql - MySql InnoDB 表更新单行需要 1+ 秒
- c# - 如果是另一个具有相同名称的表单打开 C#,则关闭表单
- python - 在这种情况下,我需要树数据类型吗?[井字游戏]
- node.js - curl 中的“troute=t1”cookie 设置是什么?以及如何在 NodeJS 中模拟它
- node.js - 是否可以将 Firestore 查询表示为字符串
- windows - 在 cmake 中获取 Windows 可执行文件的运行时依赖项并安装它们
- google-sheets - 如何修改 Google 表格中重复条目的值
- javascript - 如何保护 API 端点?
- elasticsearch - elastic apm,关闭ssl验证