4

我正在使用 java.nio.channels.Selector 并且我想为每个已准备好读/写/接受的 selectedKey 创建一个单独的线程,但我想确保同一个套接字永远不会被两个不同的同时线程。最好的方法是什么?我想在创建将处理它的套接字的线程之前取消每个 selectedKey,并在线程完成它的生命后将套接字重新注册到选择器,但我/我不确定这会有多有效....

4

2 回答 2

9

Doug Lea 有一个关于 Java 中可扩展 I/O的非常好的演示文稿,我在构建服务器时遵循了该演示文稿。我采取以下方法:

我的“反应器”中有一个 I/O 线程,它只执行 I/O(以及非常基本的解码/编码);它只是在字节和消息对象之间进行转换,然后将传入的消息对象传递给线程池以进行业务逻辑处理。我强烈推荐这种方法 -除非您的 I/O 线程变得饱和,否则不需要多个 I/O 线程,我想大多数 I/O 瓶颈是因为它发生在该线程上的其他处理。

如果您可以证明您的 I/O 线程已饱和,您可以遵循演示文稿中建议的“多反应器”模式,即主反应器接受传入连接,然后将它们交给执行处理的子反应器。每个子反应器在总连接的一个子集之间多路复用,因此不存在多个线程与给定SelectionKey.

于 2010-01-08T12:41:33.177 回答
1

我认为为每个套接字创建一个单独的线程最终可能会太多。此外,创建一个新Thread的执行时间有点昂贵。您应该通过使用线程池来限制活动线程的数量并限制新线程的创建。java.util.concurrent.Executors提供创建固定线程池的能力。http://java.sun.com/docs/books/tutorial/essential/concurrency/pools.html中的详细信息。

如果您想保护它免受一次被多个线程击中的套接字,我会考虑最简单的排除:锁定套接字对象。可能有更有效的策略,但可能没有更简单或更万无一失的策略。

更新

如果在一些较早返回的套接字仍在处理中时完成了另一个选择,则最终可能会导致线程相互干扰。通过锁定关闭其他线程是可能的,但不是一个真正优雅的解决方案(对不起)。

我能想到的两种选择:

  • 在启动处理线程之前取消注册通道,并在处理活动结束时重新注册它。听起来很笨拙,但应该完成工作。

  • 维护您自己的进行中通道的数据结构,例如 a Set,并在将其提供给线程之前将新发现的就绪通道添加到该集合,并在从线程返回之前将其删除。处理选择集中的通道时,忽略集中已存在的任何通道。该集合的所有使用都需要同步。

于 2010-01-08T12:19:00.270 回答