对于我的Java项目,我想并行执行两个任务并组合它们的结果。问题是我必须在每次执行多次调用的方法中执行此操作(可能是 1000 次或更多)。我尝试使用 ExecutorService 及其线程池,但是如果我将 ExecutorService 声明为类字段并开始向其提交工作,它会在某个时刻抛出
java.lang.OutOfMemoryError: unable to create new native thread
所以我决定使用这段代码:
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Integer> taskOne = executor.submit(new Callable<Integer> () {
public Integer call() throws Exception {
//Do work...
return 0;
}
});
Future<Integer> task2 = executor.submit(new Callable<Integer> () {
public Integer call() throws Exception {
//Do work...
return 0;
}
});
task1.get();
task2.get();
executor.shutdown();
while(!executor.isTerminated());
问题在于,使用这种新方法,与单线程解决方案相比,应用程序性能甚至更差。我认为这是由于在每个方法调用上创建新线程池的开销。
所以,我的问题是:有没有办法执行相同的任务,但不必创建和关闭每个方法调用的线程池?
谢谢。
我不一定说它本身是一个劣质的设计,但需要考虑一些事情。 就个人而言,我喜欢线程池,如果使用得当,它是一个强大的解决方案。
性能下降的原因是,在固定池大小为 2 的情况下,一次只能运行两个线程。 您可能需要一个更大的数字 - 可能是 10,可能是 100 - 但至少你可以构建它,这样你就不会耗尽内存。
也就是说,如果原始设计因为创建了太多线程而耗尽内存,您肯定需要限制一次活动的线程数,因此您的性能将受到一些影响......它只是不需要全有或全无。
您还可以使用无限制的执行程序线程池,假设正在运行的线程确实会及时返回,这样您仍然不会最大化线程。 如果线程以某种确定性的方式返回,则当线程返回到池中时,该线程立即可用于下一个可用任务,而无需创建某些内容。 所以这很好。
综上所述,查看异常消息,无论您在 Java 中做了什么,都可能无法在操作系统级别创建更多线程。 我有一个在 CentOS 上运行的服务,我合法地不得不增加每个进程允许的线程数(在这种情况下,一个运行我的服务的 Java 进程),因为有一个最大值,我正在达到它。 如果你正在运行一些 linux 风格,那也可能就是它,特别是因为它看起来线程本身除了返回之外什么都不做。
您描述的类字段解决方案听起来像是一个更好的设计。那个游泳池的大小是多少?您应该调试OutOfMemoryError
,而不是切换到较差的设计。