【问题标题】:Best way to reuse a Runnable重用 Runnable 的最佳方法
【发布时间】:2010-05-05 14:46:41
【问题描述】:

我有一个实现Runnable 的类,并且目前正在使用一个执行器作为我的线程池来运行任务(将文档索引到 Lucene 中)。

executor.execute(new LuceneDocIndexer(doc, writer));

我的问题是我的 Runnable 类创建了许多 Lucene Field 对象,我宁愿重用它们,然后每次调用都创建新对象。重用这些对象的最佳方法是什么(Field 对象不是线程安全的,所以我不能简单地将它们设为静态) - 我应该创建自己的ThreadFactory 吗?我注意到一段时间后程序开始急剧下降,我唯一能想到的就是它的 GC 开销。我目前正在尝试分析该项目以确保这甚至是一个问题 - 但现在让我们假设它是。

【问题讨论】:

  • 我之前没有 Lucene 的经验;但是可以使用Field 对象池吗?
  • 每个Field 对象都是不同的(比如每个文档有 6 个),因此使用一个通用对象池是行不通的。我也不想跟踪需要与我的线程池大小相同的 6 个对象池 - 所以如果我更改线程池大小,我必须更改对象池大小 - 听起来不太可维护。
  • 应该优化 GC 以处理快速消失的小分配,因为它是如此常见的用例。我对 Lucene 不熟悉,但如果 Field 对象足够小且寿命足够短,那么它们很有可能被 JIT 分配到堆栈上,甚至永远不会到达 GC。
  • 你想重用什么? Field 对象,还是 Runnables ?
  • 您需要进行分析,以便您可以识别问题,而不是猜测是大量的 Field 对象导致了问题。否则,您可能会做很多工作却看不到可衡量的改进。就您的问题而言,您是否考虑过享元模式是否适合?

标签: java multithreading concurrency lucene


【解决方案1】:

您的问题询问如何重用 Runnable,因此我将忽略其他细节并简单地回答该问题。

如果您使用的是 ThreadPoolExecutor,则可以使用 [ThreadPoolExecutor#afterExecute][1] 方法将 Runnable 对象返回到“缓存”Runnables 的池/队列。

[1]:http://java.sun.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html#afterExecute(java.lang.Runnable,java.lang.Throwable)

【讨论】:

  • 你是唯一一个真正回答了这个问题并且没有开始谈论情况的人,这真是太棒了。 +1!
【解决方案2】:

Runnable 对象是可重用的。它不是线程对象。

最好的方法?这是你的方式:-)

我认为这更像是一个 lucene 问题而不是一个可运行的问题。

【讨论】:

  • 我认为你倒退了。
  • @Gandalf:这张海报绝对正确。你的问题是Runnable 的实现不是线程安全的。如果是这样,您肯定可以将它传递给执行者两次(或更多次)。
【解决方案3】:

您可能需要做更多的基准测试来确定导致速度变慢的原因。

我敢打赌,您的问题与创建 Field 实例无关。 Field 没有任何终结器,它们也不是为池化而设计的。

【讨论】:

    【解决方案4】:

    现在我决定只使用一个简单的 Producer->Consumer 模型。我将BlockingQueue 传递给每个索引器,而不是要索引的文档,然后让程序的主驱动程序将新文档添加到该队列中。然后,索引器从那个 [有界] 队列中获取数据,并重用 Field 对象并共享线程安全的 IndexWriter

    我确实找到了一个我可能没有打电话给HttpMethod.releaseConnection() 的地方,所以这可能会导致我的记忆问题(不确定)。

    【讨论】:

      猜你喜欢
      • 2011-02-25
      • 2012-09-28
      • 2017-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-02
      相关资源
      最近更新 更多