在我的应用程序中,我使用多个线程来处理客户端连接。
我在调试时发现了一个非常奇怪的行为 - 我有一个SelectionKey,它通过调用(使用调试器)其interestOps()方法返回值为1(READ),但是当我将数据发送到与该键对应的套接字时选择器没有醒来..
如果使用调试器,我将特定选择键的兴趣操作更改为1(即使它为1),选择器会突然对该更改做出反应。
我在给定时间只有一个线程处理连接但该线程并不特定于该连接,如果我禁用多线程(将线程池设置为大小为1),则此问题从未发生过。
通过查看SelectionKey类文档 - 这个方法应该是线程安全的 - 我错过了什么?
答案 0 :(得分:3)
这不是线程安全的问题。如果当前正在进行select()
,则它已经读取了所有已注册密钥中的所有interestOps,并且正在选择那些读取时的值:这些值将传递给操作系统和操作系统阻止操作正在进行中。在select操作的中间更改interestOps
不会影响该选择操作,只会影响下一个操作。
答案 1 :(得分:2)
在我将所有更改移到interestOps上以便在选择器线程上完成后问题得以解决 - 所以我认为interestOps(int)不是线程安全的。
修改强>
通过将所有的interestOps更改移动到选择器线程,我也获得了30%的加速 - 不知道为什么,但这是我的测试之间唯一的变化..
答案 2 :(得分:0)
我参加派对有点晚了,但对于像我这样的人来说,将来会访问:
"多个并发线程可以安全地使用选择键。通常,读取和写入兴趣集的操作将与选择器的某些操作同步。"
答案 3 :(得分:0)
(对不起,您的评论很晚,但是我认为最好澄清一下) 如javadoc所说(https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SelectionKey.html):
此同步的确切执行方式取决于实现方式:在幼稚的实现方式中,如果选择操作已在进行中,则兴趣组的读写可能会无限期地阻塞;在高性能的实现中,读取或写入兴趣集可能会短暂阻塞(如果有的话)。无论如何,选择操作将始终使用该操作开始时当前的兴趣设置值。
因此,要完全理解原始问题,我们需要知道它发生在哪个环境(操作系统,内核版本,jdk版本)中。
(抱歉,我应该以内嵌评论的形式发送它,但不允许这样做)
修改:
根据更现代的jdk的文档(java 11+),SelectionKey应该是线程安全的:https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/SelectionKey.html
更具体地说,SelectionKey#interestOps(int ops)
应该
可以随时调用。如果在选择操作进行时调用此方法,则此方法对该操作无效;下次选择操作将看到对密钥兴趣集的更改。