1

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

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

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

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

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

4

4 回答 4

1

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

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

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

于 2009-12-22T02:06:21.140 回答
0

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

于 2009-12-22T05:54:57.433 回答
0

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

于 2009-12-22T04:15:13.673 回答
0

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

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

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

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

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

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

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

于 2016-03-16T10:33:52.593 回答