给定应用程序的多个 SingleThreadExecutors...是个好主意吗?

发布于 2024-08-15 05:02:31 字数 792 浏览 6 评论 0原文

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

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

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

我对必须创建这么多 SingleThreadExecutor 的想法感到不舒服。我宁愿尝试使用 SingleThreadExecutor 池,如果这有意义并且合理的话(任何现成的、之前见过的类/模式?)。我在这里阅读了许多关于建议使用 SingleThreadExecutor 的文章,但是相同的池怎么样?

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

This question is about the fallouts of using SingleThreadExecutor (JDK 1.6). Related questions have been asked and answered in this forum before, but I believe the situation I am facing, is a bit different.

Various components of the application (let's call the components C1, C2, C3 etc.) generate (outbound) messages, mostly in response to messages (inbound) that they receive from other components. These outbound messages are kept in queues which are usually ArrayBlockingQueue instances - fairly standard practice perhaps. However, the outbound messages must be processed in the order they are added. I guess use of a SingleThreadExector is the obvious answer here. We end up having a 1:1 situation - one SingleThreadExecutor for one queue (which is dedicated to messages emanating from one component).

Now, the number of components (C1,C2,C3...) is unknown at a given moment. They will come into existence depending on the need of the users (and will be eventually disposed of too). We are talking about 200-300 such components at the peak load. Following the 1:1 design principle stated above, we are going to arrange for 200 SingleThreadExecutors. This is the source of my query here.

I am uncomfortable with the thought of having to create so many SingleThreadExecutors. I would rather try and use a pool of SingleThreadExecutors, if that makes sense and is plausible (any ready-made, seen-before classes/patterns?). I have read many posts on recommended use of SingleThreadExecutor here, but what about a pool of the same?

What do learned women and men here think? I would like to be directed, corrected or simply, admonished :-).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

披肩女神 2024-08-22 05:02:31

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

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

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

If your requirement is that the messages be processed in the order that they're posted, then you want one and only one SingleThreadExecutor. If you have multiple executors, then messages will be processed out-of-order across the set of executors.

If messages need only be processed in the order that they're received for a single producer, then it makes sense to have one executor per producer. If you try pooling executors, then you're going to have to put a lot of work into ensuring affinity between producer and executor.

Since you indicate that your producers will have defined lifetimes, one thing that you have to ensure is that you properly shut down your executors when they're done.

一梦等七年七年为一梦 2024-08-22 05:02:31

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

Messaging and batch jobs is something that has been solved time and time again. I suggest not attempting to solve it again. Instead, look into Quartz, which maintains thread pools, persisting tasks in a database etc. Or, maybe even better look into JMS/ActiveMQ. But, at the very least look into Quartz, if you have not already. Oh, and Spring makes working with Quartz so much easier...

趴在窗边数星星i 2024-08-22 05:02:31

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

I don't see any problem there. Essentially you have independent queues and each has to be drained sequentially, one thread for each is a natural design. Anything else you can come up with are essentially the same. As an example, when Java NIO first came out, frameworks were written trying to take advantage of it and get away from the thread-per-request model. In the end some authors admitted that to provide a good programming model they are just reimplementing threading all over again.

月依秋水 2024-08-22 05:02:31

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

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

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

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

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

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

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

It's impossible to say whether 300 or even 3000 threads will cause any issues without knowing more about your application. I strongly recommend that you should profile your application before adding more complexity

The first thing that you should check is that number of concurrently running threads should not be much higher than number of cores available to run those threads. The more active threads you have, the more time is wasted managing those threads (context switch is expensive) and the less work gets done.

The easiest way to limit number of running threads is to use semaphore. Acquire semaphore before starting work and release it after the work is done.

Unfortunately limiting number of running threads may not be enough. While it may help, overhead may still be to great, if time spent per context switch is major part of total cost of one unit of work. In this scenario, often the most efficient way is to have fixed number of queues. You get queue from global pool of queues when component initializes using algorithm such as round-robin for queue selection.

If you are in one of those unfortunate cases where most obvious solutions do not work, I would start with something relatively simple: one thread pool, one concurrent queue, lock, list of queues and temporary queue for each thread in pool.

Posting work to queue is simple: add payload and identity of producer.

Processing is relatively straightforward as well. First you get get next item from queue. Then you acquire the lock. While you have lock in place, you check if any of other threads is running task for same producer. If not, you register thread by adding a temporary queue to list of queues. Otherwise you add task to existing temporary queue. Finally you release the lock. Now you either run the task or poll for next and start over depending on whether current thread was registered to run tasks. After running the task, you get lock again and see, if there is more work to be done in temporary queue. If not, remove queue from list. Otherwise get next task. Finally you release the lock. Again, you choose whether to run the task or to start over.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文