6

我正在使用java.util.concurrent.ExecutorService通过调用获得的Executors.newSingleThreadExecutor(). 这ExecutorService有时会停止处理任务,即使它没有被关闭并继续接受新任务而不抛出异常。OutOfMemoryError最终,它建立了足够多的队列,以至于我的应用程序异常关闭。

文档似乎表明,这个单线程执行器应该通过在必要时启动一个新的工作线程来替换一个已经死亡的工作线程来避免任务处理错误。我错过了什么吗?

4

3 回答 3

10

听起来您有两个不同的问题:

1)您过度使用工作队列。你不能一直往队列里塞新任务,而不考虑任务执行者的消耗率。您需要找出一些逻辑来了解何时阻止新添加到工作队列中。

2)任务线程中任何未捕获的异常都可以完全杀死线程。发生这种情况时,ExecutorService 会启动一个新线程来替换它。但这并不意味着您可以忽略导致线程首先死亡的任何问题!找到那些未捕获的异常并捕获它们!

这只是一种预感(因为您的帖子中没有足够的信息来了解其他情况),但我不认为您的问题是任务执行者停止处理任务。我的猜测是它处理任务的速度不如您创建它们的速度快。(而且您的任务有时会过早终止这一事实可能与问题无关。)

至少,这是我使用线程池和任务执行器的经验。


好的,根据您的评论,这是另一种听起来可行的可能性(一切都会顺利运行几个小时,直到突然停止)......

您的任务线程之间可能会出现罕见的死锁。大多数时候,你很幸运,僵局并没有表现出来。但偶尔,您的两个或多个任务线程会进入等待释放另一个线程持有的锁的状态。此时,无法进行更多任务处理,并且您的工作队列将堆积起来,直到您收到 OutOfMemoryError。

这是我诊断该问题的方法:

消除任务线程之间的所有共享状态。首先,这可能需要每个任务线程制作它所需的所有共享数据结构的防御性副本。一旦你这样做了,就应该完全不可能遇到死锁。

此时,逐渐重新引入共享数据结构,一次一个(具有适当的同步)。在每次微小修改后重新运行您的应用程序以测试死锁。当您再次遇到这种崩溃情况时,请仔细查看共享资源的访问模式并确定您是否真的需要共享它。

就我而言,每当我编写处理带有线程池和执行器的并行任务的代码时,我总是试图消除这些任务之间的所有共享状态。就应用程序而言,它们也可能是完全自主的应用程序。寻找死锁是一种拖累,根据我的经验,消除死锁的最佳方法是让每个线程拥有自己的本地状态,而不是与其他任务线程共享任何状态。

祝你好运!

于 2008-12-05T19:01:37.327 回答
3

我的猜测是您的任务会无限期地阻塞,而不是死亡。您是否有证据(例如任务结束时的日志声明)表明您的任务已成功完成?

这可能是死锁,或者是与某个阻塞的外部进程的交互。

于 2008-12-05T19:52:20.517 回答
1

尽管您没有留下足够的细节来确定,但我会尝试的第一件事是让您的任务在顶层捕获“异常”并记录消息。

我知道这看起来不对,但偶尔(取决于很多变量)我处理过代码,其中线程中发生的事情会引发异常并且它永远不会被记录,或者它只是没有出现在控制台——但“执行”代码退出它的顶级循环或任何导致您的任务运行的代码。

我想我只是说,确保你的任务没有抛出异常。

于 2008-12-05T19:02:22.943 回答