从类加载器线程启动时,为什么ExecutorService不处理提交的任务?

时间:2019-01-06 09:53:56

标签: java multithreading executorservice classloading

我有一个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返回(预期会在1秒后返回)。

如果我通过注释(然后注释掉另一个)并切换到标记为“工作替代”的代码并注释掉static中的Singleton块,则代码将按预期执行({{ 1}}和method1被调用,并根据输出以相反的顺序返回。)

因为“可行的替代方案”可以解决问题,并且我可以忍受,所以我正在寻找这种行为的解释。

我打算使用method2而不是Class.forName是为了能够向Singleton.init添加更多静态初始化任务,而无需考虑预初始化是否覆盖了它们常规。我同意整个设置并不理想。

1 个答案:

答案 0 :(得分:3)

您的前进方向错误。首先,不要在类初始化时进行繁琐的计算。在类初始化完成之前,将限制对类方法的调用。这个想法不是要向类方法显示尚未初始化的变量。只能执行直接从静态初始化程序调用的方法,否则它们将被阻止。在您的情况下,将阻止并行任务对method1method2的调用。

通常,请尽可能避免使用静态变量。而是创建对象的实例。对于给定的情况,创建一个Singleton类的实例,并将所有变量从静态字段转换为实例字段。

最后,不要只运行线程来调用

  thread.start();
  thread.join();

更好地直接调用传递给线程的方法为Runnable