我问这个问题是因为我正在创建大量的执行程序服务,虽然我可能已经在需要调查的地方发生内存泄漏,但我认为最近对以下代码的更改实际上会使其恶化,因此我想确认发生了什么:
@FunctionalInterface
public interface BaseConsumer extends Consumer<Path> {
@Override
default void accept(final Path path) {
String name = path.getFileName().toString();
ExecutorService service = Executors.newSingleThreadExecutor(runnable -> {
Thread thread = new Thread(runnable, "documentId=" + name);
thread.setDaemon(true);
return thread;
});
Future<?> future = service.submit(() -> {
baseAccept(path);
return null;
});
try {
future.get();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
} catch (ExecutionException ex) {
throw new RuntimeException(ex);
}
}
void baseAccept(final Path path) throws Exception;
}
然后在(通常)N = 2个线程的另一个线程池上调用此Consumer<Path>
,我不确定这是否相关。
问题是:ExecutorService service
完成后,BaseConsumer#accept
是否超出范围和会收集垃圾?
答案 0 :(得分:18)
在
BaseConsumer.accept()
完成后,ExecutorService服务是否超出范围并收集垃圾?
是。
实际上,关联的线程池也应该是垃圾收集......最终。
由ExecutorService
Executors.newSingleThreadExecutor()
的实例创建的FinalizableDelegatedExecutorService
。该类具有finalize()
方法,该方法在包装的shutdown()
对象上调用ExecutorService
。如果所有未完成的任务实际终止,服务对象将关闭其线程池。
(AFAIK,这没有指定。但它是根据源代码实现的,在Java 6以后。)
添加finally {service.shutdown();在future.get()中的try-catch帮助更快地检索资源? (不一定是垃圾收集服务对象)。
是的。调用shutdown()
会导致线程在未完成的任务完成后立即释放。该过程立即开始,而如果你把它留给垃圾收集器,它将在调用终结器之前启动。
现在,如果资源只是“普通”的Java对象,那就无所谓了。但在这种情况下,您要回收的资源是Java线程,并且具有关联的操作系统资源(例如本机线程)和非平凡的堆外存储器块。所以这可能是值得的。
但是如果你想要优化它,也许你应该创建一个长期存在的ExecutorService
对象,并在多个“消费者”实例中共享它。
答案 1 :(得分:7)
我想让执行发生在命名线程上,它与更容易记录有关。在任何一种情况下,此代码都应该有效。
你可以更简单/更快地完成这项工作
Thread t = Thread.currentThread();
String name = t.getName();
try {
t.setName("My new thread name for this task");
// do task
} finally {
t.setName(name);
}
这样您就可以使用命名线程而无需创建新线程。