【问题标题】:concurrent handling of java.nio.channels.Selectorjava.nio.channels.Selector 的并发处理
【发布时间】:2011-01-02 21:37:15
【问题描述】:

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

【问题讨论】:

    标签: java multithreading sockets selector


    【解决方案1】:

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

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

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

    【讨论】:

    • 我的第一反应也是推荐这个。不过,我不确定这些读/写/接受操作中的一些是否会在“准备好”的情况下不会引起一些延迟。但如果 Doug Lea 也建议这样做,这可能是一种很好的做事方式。
    【解决方案2】:

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

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

    更新

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

    我能想到的两种选择:

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

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

    【讨论】:

    • 您好,感谢您的回复。我正在使用线程池。我的问题是套接字仍然注册到选择器,同时仍然由池中的线程处理。这可能会导致不必要地为线程池创建新任务,线程池将等待套接字释放,才发现必要的工作已经完成。这是我试图阻止的第二个任务的创建。
    • 很抱歉这么慢才赶上来。我已经用更多建议更新了我的答案。
    猜你喜欢
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 2021-12-22
    相关资源
    最近更新 更多