考虑一个请求-响应协议。
我们生成一个线程来执行一个select()
循环,以便在接受的非阻塞 上进行读取和写入SocketChannel
。这可能看起来像
while (!isStopped()) {
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey selectedKey = selectedKeys.next();
selectedKeys.remove();
Context context = (Context) selectedKey.attachment();
if (selectedKey.isReadable()) {
context.readRequest();
} else /* if (selectedKey.isWritable()) */ {
context.writeResponse();
}
}
}
whereContext
只是相应的容器SocketChannel
,缓冲区和读取它并从中写入的逻辑。可能readRequest
看起来像
public void readRequest() {
// read all content
socketChannel.read(requestBuffer);
// not interested anymore
selectionKey.interestOps(0);
executorService.submit(() -> {
// handle request with request buffer and prepare response
responseBuffer.put(/* some response content */); // or set fields of some bean that will be serialized
// notify selector, ready to write
selectionKey.interestOps(SelectionKey.OP_WRITE);
selectionKey.selector().wakeup(); // worried about this
});
}
换句话说,我们从套接字通道读取数据,填充一些缓冲区并将处理交给其他线程。该线程进行处理并准备将其存储在响应缓冲区中的响应。然后它通知选择器它想要写入并唤醒它。
JavadocSelector#wakeup()
没有提到任何发生前的关系,所以我担心选择器线程可能会看到响应缓冲区(或某些中间对象)处于不一致的状态。
这是可能的情况吗?如果是,那么将响应写入循环线程SocketChannel
的正确方法是什么?Selector
(通过某个字段发布响应volatile
?使用SelectionKey
附件?其他形式的同步?)