【问题标题】:ExecutorService and OutOfMemoryError: unable to create new native thread while using ExecutorExecutorService 和 OutOfMemoryError:使用 Executor 时无法创建新的本地线程
【发布时间】:2016-10-26 02:19:45
【问题描述】:

我有一个基于 JSP 的 (java) Web 应用程序。在这个应用程序中,我可以通过执行外部命令,根据 IP 向机器 (PC) 询问它们的状态和实际操作系统。

为了加速请求,我想使用线程同时询问更多机器,即 ExecutorService。

相应视图的 preRenderView 监听器设置为这个方法,我收集所有必须显示的数据。这里我初始化执行器,声明为私有静态类字段(private static ExecutorService executor):

public void selectData( ComponentSystemEvent event )
{
  AmtRoomMachinesListController.executor = Executors.newFixedThreadPool(20);

  AmtRoomMachinesListModel amtRoomMachinesListModel = (AmtRoomMachinesListModel)getModel();

  List< ListRow > listRows = fetchListRows( amtRoomMachinesListModel );
...
}

fetchListRow 中调用执行器并提交可调用对象。然后执行器被关闭并终止:

private List< ListRow > fetchListRows( AmtRoomMachinesListModel amtRoomMachinesListModel )
{
    ...
    List< ListRow > listRows = Collections.synchronizedList( new ArrayList< ListRow >() );

    for ( Machine machine : room.getRoomPCs() )
    {
        executor.submit( new AmtcWorker( listRows, machine, amtRoomMachinesListModel ) );
    }

    executor.shutdown();

    try
    {
        executor.awaitTermination( 20, TimeUnit.SECONDS );
    }
    catch ( InterruptedException e )
    {
        throw new BootrobotException( ExceptionType.AMTC_ERROR, "command", "Waiting for thread termination", "error", e.getMessage() );
    }

    ((ThreadPoolExecutor)executor).purge();

    LOGGER.info( "Executor is shut down: " + executor.isShutdown() );
    LOGGER.info( "Executor is terminated: " + executor.isTerminated() );

    sortListRows( listRows );

    return listRows;
}

我的问题是进程/线程的数量不断增加,一段时间后我得到 OutOfMemory 异常。每次调用selectData,进程数会根据提示的机器数增加。

我是线程新手,但我认为执行程序会在调用 executor.shutdown() 或 executor.awaitTermination 或 executor.purge() 时通过终止/杀死生成的线程来处理它们。

我错过了什么?

【问题讨论】:

  • 为每个请求创建一个新的执行器是可疑的。您可能应该创建一个执行程序并将其用于所有请求。
  • 你有没有试过在 Executor 完成后清空引用?除此之外,我第二个 @OldCurmudgeon 。您可能应该创建一个“全局”ExecutorService 并使用InvokeAll
  • 您的代码中有日志记录语句。日志显示什么?等等——显然,你的executor 是你的类中的一个static 变量,你可以从实例方法中使用它。那是要求混乱。每次调用 selectData 时都会覆盖该变量,即使其他对象仍在使用它。因此,您无法控制哪个执行器将被关闭(或多久关闭一次)以及哪个永远不会关闭。
  • 你不应该使用 AmtRoomMachinesListController.executor.shutdown(); ?或者你能给我们函数 fetchListRows 的完整代码
  • 我同意@OldCurmudgeon,你应该创建一次执行器而不是关闭它,如果你想等待结果,你可以使用 CountDownLatch 例如

标签: java multithreading out-of-memory executorservice


【解决方案1】:

我的建议是只创建一个线程池。线程池旨在管理您的线程。如果你每次调用一个方法都创建一个线程池,那基本上和每次调用一个方法创建线程一样糟糕,但是如果你仍然坚持要创建多个线程池,那你为什么不试试这个而是

private List< ListRow > fetchListRows( AmtRoomMachinesListModel amtRoomMachinesListModel )
{
   ExecutorService executor = Executors.newFixedThreadPool(20);
...
List< ListRow > listRows = Collections.synchronizedList( new ArrayList< ListRow >() );

for ( Machine machine : room.getRoomPCs() )
{
    executor.submit( new AmtcWorker( listRows, machine, amtRoomMachinesListModel ) );
}

executor.shutdown();

try
{
    executor.awaitTermination( 20, TimeUnit.SECONDS );
}
catch ( InterruptedException e )
{
    throw new BootrobotException( ExceptionType.AMTC_ERROR, "command", "Waiting for thread termination", "error", e.getMessage() );
}

((ThreadPoolExecutor)executor).purge();

LOGGER.info( "Executor is shut down: " + executor.isShutdown() );
LOGGER.info( "Executor is terminated: " + executor.isTerminated() );

sortListRows( listRows );

return listRows;
}

刚刚本地化了执行器

【讨论】:

    【解决方案2】:

    使用一个线程池。另外,您是否确认 awaitTermination 函数正在返回 true? awaitTermination 没有在 20 秒内完成并返回 false 的可能性很小。新的线程池不断创建,而旧的线程池没有被 GC 并最终耗尽内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多