如何在ExecutorService的执行者之间进行同步

时间:2018-08-14 20:26:16

标签: java multithreading java.util.concurrent

我有一个客户端套接字列表,通常大小约为2000。这些客户端是动态的,它们来来往往。

我有一个ExecutorService,带有一个固定的线程池,其中包含处理这些线程的32个线程。该执行程序服务负责解码并发送要发送到这2000个客户端的消息。

我想防止执行程序服务的两个(或多个)线程同时处理同一客户端。

一种方法可能是引入另一个簿记线程(因此我最终得到32 + 1个线程),该线程负责在对应于同一客户端的先前消息为ExecutorService.submit(mesage)时调用完成。但是我不确定这是否会带来瓶颈,这意味着这个新引入的簿记线程无法跟上提交消息的时间。

理想情况下,我不希望预先将线程预先分配给一组客户端,因为消息负载在各个客户端之间分布不均。事先也不知道。

有哪些方法?它们是由java.util.concurrent功能提供的吗?

更新

这是一个简短的摘要,因为评论指出存在一些误解:

  • 我不希望每个客户端有一个线程,因为最终会有2000个线程。

  • 理想情况下,我不想将线程预先分配给一组客户端,因为消息率不是在所有客户端之间平均分配的,而且事先也不知道。

  • 必须保留消息顺序。

  • 我认为线程A等待线程B并不好,因为B已经在向同一客户端发送消息。换句话说,在任何时候,只有一个线程正在处理一个客户端。

3 个答案:

答案 0 :(得分:1)

当线程(A)开始处理消息(#1)时,它需要向共享管理器对象注册客户端ID。对于每个注册客户,都有一个队列。

当另一个线程(B)开始为同一客户端处理消息(#2)时,注册将检测到线程A已经在处理,并将消息#2添加到客户端的队列中。然后,线程B将停止并处理下一条消息。

当线程A处理完消息#1后,它将尝试注销,但是由于消息#2处于队列中,线程A将开始处理该消息。之后,当它再次尝试注销时,没有排队的消息,线程将停止并处理下一条消息。

要由管理者代码正确地同步访问,因此第二条消息可以由线程B处理,也可以移交给线程A,而不会丢失。

以上逻辑确保线程B将不等待线程A,即没有空闲时间,并且尽快处理消息#2,即以最小的延迟,而不会为同一客户端处理两个消息。时间。

每个客户的消息顺序 被保留。在全球范围内,当然不会保留消息顺序,因为消息#2的处理会延迟。

请注意,每个线程只有一个队列,因此只有32个队列,并且只有“重复”消息是队列,因此所有队列通常都为空。


更新

示例:为了在此处进行标识,消息被命名为clientId.messageId,其中messageId是全局的。

消息按以下顺序提交给执行器(3个线程):

1.1, 2.2, 1.3, 2.4, 3.5, 1.6

  1. 线程A拾取1.1并开始处理。

  2. 线程B拾取2.2并开始处理。

  3. 线程C选取1.3,将其添加到线程A的队列中,然后返回。

  4. 线程C选取2.4,将其添加到线程B的队列中,然后返回。

  5. 线程C选取3.5并开始处理。

  6. 线程A完成了消息1.1,并开始处理1.3

  7. 线程C完成消息3.5并返回。

  8. 线程C选取1.6,将其添加到线程A的队列中,然后返回。
    线程C现在处于空闲状态。

  9. 线程B完成了消息2.2,并开始处理2.4

  10. 线程A完成了消息1.3,并开始处理1.6

  11. 线程B用消息2.4完成并返回。
     线程B现在处于空闲状态。

  12. 线程A使用消息1.6完成并返回。
     线程A现在处于空闲状态。

答案 1 :(得分:0)

您要顺序处理每个客户端的消息,但同时又不想为每个客户端分配单独的线程。这是使用Actor model的确切用例。演员就像轻量级的线程。它们不像通常的线程那样强大,但是非常适合像您这样的可重复任务。 如果您发现java actor libraries被Google发现太重了,则可以在我的Github存储库中使用most compact actor implementation,或者查看我的异步库df4j中包含的扩展的actor实现。

答案 2 :(得分:-1)

使每个线程服务都有自己的队列。编号插座。将每个请求放入队列[socket num%线程数]。

这将确保按顺序和顺序处理来自特定套接字的请求。

不幸的是,您将无法通过这种方式实现负载平衡。

或者,使用并发哈希表存储正在提供的套接字。如果线程为当前正在处理的套接字请求提供服务,则只需将请求放回队列即可。