Java 使用ExecutorService时正确管理内存

Java 使用ExecutorService时正确管理内存,java,multithreading,concurrency,garbage-collection,Java,Multithreading,Concurrency,Garbage Collection,有一个名为“ConcurrencyUtils”的实用程序类,它在内部使用ExecutorService private static final ExecutorService executor = Executors.newCachedThreadPool(new CommonPoolThreadFactory()); 正如您所看到的,它使用了一个线程池,可以根据需要添加线程。问题是,如果该池中至少有一个线程(如果至少使用了一次util),那么当主方法执行完成时,应用程序并没有关闭,因为线程

有一个名为“ConcurrencyUtils”的实用程序类,它在内部使用ExecutorService

private static final ExecutorService executor = Executors.newCachedThreadPool(new CommonPoolThreadFactory());
正如您所看到的,它使用了一个线程池,可以根据需要添加线程。问题是,如果该池中至少有一个线程(如果至少使用了一次util),那么当主方法执行完成时,应用程序并没有关闭,因为线程仍然处于活动状态,并且没有被垃圾收集

最初我试着使用

public static void close(){
    executor.shutdown();
}
并在程序自然停止执行时调用它。但不能保证main方法是最后一个要完成的方法,并且在应用程序终止之前还可以完成其他任务,因此我不知道在哪里调用“close()”来清除util中的线程

我想知道社区是否能提示我一种更聪明/更干净的方法来实现这一点

ConcurrencyUtil打算在任何地方都可以使用,我不能创建它的实例,我需要它通过静态而不是实例方法提供接口


更新 让我详细解释一下这个例子,ConcurrencyUtil有自己的池实现,它提供了有用的方法来链接几个可运行程序并并行执行它们,或者让其中一些等待其他程序完成然后执行。。。重要的是,它应该是一个实用程序类,这样无论何时您都可以使用async()或chain(),而不必担心它是如何工作的

下面是示例代码:

public static void main(String[] args) {
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            System.out.println("hoooooooooooook");
        }
    });


    ConcurrencyUtil.chain()
            .add(()->System.out.println("task 1"))
            .add(()->System.out.println("task 2"))
            .execute()
            .join();

    System.out.println("main finish");
}
当它执行时,我将有:

created new thread with name [common-pool : thread-1]
created new thread with name [common-pool : thread-2]
task 1 
task 2 
main finish
正如您所看到的,线程池创建两个线程并执行并发任务,但这些工作线程仍然处于活动状态(处于睡眠状态)等待新任务,这就是为什么在主执行完成后应用程序仍在运行。。。关闭钩子也不起作用,因为(我想)应用程序被认为是活动的

既然我不能保证main方法是我的退出点(它可能是一个runnable,如果我删除了.join(),它就会完成执行),这就是为什么我不能将finally阻塞并关闭ConcurrencyUtil


有什么办法可以做到这一点吗

应用程序设计的正确方法是引入一个显式关闭过程,该过程在适当的地方调用。然后,此过程可以对executor服务调用
shutdown()
,然后调用
awaitTermination()
。查看
ExecutorService
的Javadoc,了解完整的代码示例


除了常规关闭协议之外,您还可以提供一个JVM关闭挂钩,它将再次启动相同的关闭过程。您永远不应该依赖这种机制,但在出现不可预知的故障时,拥有这种机制是一件好事。

使用关闭挂钩:关闭挂钩只应作为破解代码的临时解决办法。正确设计的应用程序有正确设计的关闭程序。我不知道那个类。它是否有一个构造函数允许您传入自己的ExecutorService?任何使用ExecutorService的库都应该为客户提供一种提供ExecutorService的方法。(并不是说它们都有,但如果它们都有,世界会更好。)@Vach如果您的任务需要执行任何关键清理,那么使用守护进程线程是危险的。这些线程将在关机时突然停止。正确的设计需要可中断线程。我总是在一个地方调用
shutdown()
。当然,
ThreadPool
确实定义了一个终结器方法,它在这里调用
shutdown()
。在我的例子中,shutdown钩子不起作用。它从未被执行过,不确定hook是否与这个问题有关。