【问题标题】:Multiple SingleThreadExecutors for a given application...a good idea?给定应用程序的多个 SingleThreadExecutors……好主意吗?
【发布时间】:2009-12-22 01:59:36
【问题描述】:

这个问题是关于使用 SingleThreadExecutor (JDK 1.6) 的后果。之前在这个论坛上也有过相关问题的提问和回答,但是我相信我现在面临的情况,有点不一样。

应用程序的各种组件(我们称之为组件 C1、C2、C3 等)生成(出站)消息,主要是为了响应它们从其他组件接收到的消息(入站)。这些出站消息保存在队列中,这些队列通常是ArrayBlockingQueue 实例——也许是相当标准的做法。但是,必须按照添加的顺序处理出站消息。我想使用SingleThreadExector 是显而易见的答案。我们最终遇到了 1:1 的情况 - one SingleThreadExecutor 用于 one 队列(专用于从 one 组件发出的消息)。

现在,组件的数量(C1、C2、C3...)在给定时刻是未知的。它们将根据用户的需要而存在(最终也将被处理掉)。我们说的是在峰值负载时有 200-300 个这样的组件。按照上述1:1的设计原则,我们将安排200个SingleThreadExecutors。这是我在这里查询的来源。

想到要创建这么多SingleThreadExecutor,我感到很不舒服。我宁愿尝试使用一个 SingleThreadExecutor 池,如果这是有道理的并且是合理的(任何现成的、前见的类/模式?)。我在这里阅读了很多关于推荐使用SingleThreadExecutor 的帖子,但是相同的池呢?

这里有学问的女人和男人是怎么想的?我希望得到指导、纠正或简单的警告:-)。

【问题讨论】:

  • 订单要求是全局的还是每个组件的?
  • 好问题,抱歉,我没有澄清这一点。订单要求是每个组件。

标签: java multithreading pool


【解决方案1】:

如果您的要求是按照发布的顺序处理消息,那么您只需要一个SingleThreadExecutor。如果您有多个执行器,则消息将在这组执行器中乱序处理。

如果消息只需要为单个生产者按接收顺序处理,那么每个生产者都有一个执行者是有意义的。如果您尝试汇集执行者,那么您将不得不投入大量工作来确保生产者和执行者之间的亲和力。

由于您指出您的生产者将定义生命周期,因此您必须确保的一件事是在执行者完成后正确关闭它们。

【讨论】:

  • 谢谢。您回复中的“大量工作”部分是我不安的根源。即使我能做到,拥有 200 个 SingleThreadPoolExecutors 也可能不是一个好主意。你怎么看?
  • 我的“大量工作”评论实际上是建议 200 个执行者是 正确 的方式......前提是您不能使用单个执行者。但实际需求只有自己知道。
【解决方案2】:

消息传递和批处理作业已被一次又一次地解决。我建议不要尝试再次解决它。相反,请查看 Quartz,它维护线程池、在数据库中持久化任务等。或者,也许更好地查看 JMS/ActiveMQ。但是,至少看看 Quartz,如果你还没有的话。哦,Spring 让使用 Quartz 变得更加容易......

【讨论】:

  • Quartz 确实值得一看。谢谢。 JMS 对于我所涉及的那种应用程序来说太重了;应用程序的部署特性阻碍了 Spring 的使用。
【解决方案3】:

我认为那里没有任何问题。本质上,您有独立的队列,每个队列都必须按顺序排空,每个队列一个线程是一种自然设计。你能想到的任何其他东西基本上都是一样的。例如,当 Java NIO 第一次问世时,编写框架试图利用它并摆脱每请求线程模型。最后,一些作者承认,为了提供一个好的编程模型,他们只是重新实现了线程。

【讨论】:

  • 谢谢,但是拥有一堆 SingleThreadPoolExecutors(在高峰期达到 300 个)是一个好的设计 - 这就是问题所在。这种安排对我来说当然让事情变得简单多了,但是从(最终)性能/可扩展性的角度来看,你会同意吗?非常感谢您花时间回复。
  • 200 个线程对于单机来说应该不是问题。如果你想要更健壮和可扩展的解决方案,你需要一个分布式架构,应该考虑现有的用于此类目的的框架。
【解决方案4】:

如果不了解您的应用程序的更多信息,就不可能说 300 甚至 3000 个线程是否会导致任何问题。我强烈建议您在添加更多复杂性之前先分析您的应用程序

您应该检查的第一件事是并发运行的线程数不应高于可用于运行这些线程的内核数。您拥有的活动线程越多,管理这些线程所浪费的时间就越多(上下文切换成本高昂),完成的工作就越少。

限制运行线程数的最简单方法是使用信号量。开始工作前获取信号量,工作完成后释放。

不幸的是,限制运行线程的数量可能还不够。虽然它可能会有所帮助,但如果每次上下文切换所花费的时间是一个工作单元总成本的主要部分,那么开销可能仍然很大。在这种情况下,通常最有效的方法是拥有固定数量的队列。当组件使用轮询等算法进行队列选择初始化时,您会从全局队列池中获取队列。

如果您处于最明显的解决方案不起作用的不幸情况之一,我将从相对简单的东西开始:一个线程池、一个并发队列、锁、队列列表和池中每个线程的临时队列。

将工作发布到队列很简单:添加有效负载和生产者身份。

处理也相对简单。首先,您从队列中获取下一个项目。然后你获得锁。当您锁定到位时,您检查是否有任何其他线程正在为同一生产者运行任务。如果没有,则通过将临时队列添加到队列列表来注册线程。否则,您将任务添加到现有的临时队列。最后你释放锁。现在,您可以运行任务或轮询下一个并重新开始,具体取决于当前线程是否已注册以运行任务。运行任务后,你再次获得锁,看看临时队列中是否还有更多工作要做。如果没有,则从列表中删除队列。否则得到下一个任务。最后你释放锁。同样,您可以选择是运行任务还是重新开始。

【讨论】:

    猜你喜欢
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-19
    • 2012-09-29
    • 2015-02-03
    相关资源
    最近更新 更多