2010-01-08 28 views
3

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

回答

9

有一个很好的Doug Lea的presentation约可扩展的I/O在Java中,建设我的服务器时,我紧随其后。我采取以下方法:

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

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

+0

我的第一反应是推荐这个。不过,我不确定这些读/写/接受操作中的哪些操作不会因为“准备就绪”而导致延迟。但是,如果Doug Lea也推荐这样做,这可能是一个很好的做事方式。 – 2010-01-08 12:58:02

1

我认为为每个套接字创建一个单独的线程可能会导致太多。另外,创建一个新的Thread在执行时间是很昂贵的。您应该限制活动线程的数量并通过使用线程池来限制新线程的创建。 java.util.concurrent.Executors提供了创建固定线程池的功能。详情请见http://java.sun.com/docs/books/tutorial/essential/concurrency/pools.html

如果它的套接字要防止被多个线程一次击中,我会考虑最简单的排除:锁定套接字对象。可能会有更有效的策略,但可能并非简单或更简单。

更新

如果在一些早期返回插座仍在处理其他选择完成后,你可以使用线程互相干扰而告终。通过锁定关闭其他线程是可能的,但不是真正的优雅解决方案(对不起)。

两种选择,我能想到的:

  • 注销该通道就可以开始你的处理线程之前,以及在处理活动结束时重新注册。听起来很舒服,但应该完成工作。

  • 维护您自己的正在进行中的渠道的数据结构,例如,一个Set,并在将其发送给线程之前向该集合添加新发现的通道,并在从线程返回之前将其删除。在处理来自选择集的频道时,忽略已经在该集中的任何频道。所有这一套使用都需要同步。

+0

嗨,谢谢你的回复。 我正在使用线程池。 我的问题是,套接字仍然被注册到Selector,同时仍然由池中的线程处理。 这可能会导致线程池的新任务不必要的创建,该任务将等待套接字的发布,以便发现必需的工作已经完成。 这是我试图阻止的第二项任务的创建。 – yuri 2010-01-08 12:34:24

+0

对不起,这很慢。我已经用更多建议更新了我的答案。 – 2010-01-08 12:56:14