关于AsynchronousSocketChannel内存屏障、缓冲区重复写入可见性及变量volatile必要性的技术问询
当使用AsynchronousSocketChannel.write传输消息时,我实现了这样一段Scala代码:
class Transmitter(channel: AsynchronousSocketChannel) { def transmit(bytes: Array[Byte])(onComplete: Consumer[String]): Unit = { val buffer = ByteBuffer.wrap(bytes) object handler extends CompletionHandler[Integer, Null] { override def completed(result: Integer, attachment: Null): Unit = { if result == 0 then onComplete.accept("Failure: connection closed prematurely") else if buffer.hasRemaining then channel.write(buffer, null, handler) else onComplete.accept("Successful") } override def failed(exc: Throwable, attachment: Null): Unit = onComplete.accept("Failure: connection error") } channel.write(buffer, null, handler) } }
我了解到CompletionHandler.completed方法可能在整个缓冲区内容发送完成前就被触发,这种情况下确实需要再次调用AsynchronousSocketChannel.write来发送剩余的缓冲区内容,这点应该没错吧?
但我现在有个困惑:write方法的文档提到,处理器可能在NIO2线程组的另一个线程中执行,而Buffer的文档明确说明它不是线程安全的——甚至它的状态变量都没有被声明为volatile。这就导致一个问题:我似乎没办法可靠地读取缓冲区的状态来处理剩余内容,因为可能存在内存可见性的问题。
这里要澄清的是,不存在并发问题(因为completed方法是被顺序调用的),但可见性问题是真实存在的:运行completed方法的线程,可能看不到之前调用write的线程,或者上一次执行completed的线程对缓冲区状态变量做出的修改。
而且用户层面的同步操作或者内存屏障也解决不了这个问题,因为缓冲区的状态是被write/read方法内部修改的。
这让我做出两种推测:要么AsynchronousSocketChannel.write本身已经确保了正确的内存可见性,要么NIO2 API在并发场景下存在设计缺陷(但后者看起来可能性极低)。
我的核心疑问就是:那些仅在CompletionHandler方法中被访问的变量(这些方法是被顺序调用的),到底需不需要被声明为volatile?
备注:内容来源于stack exchange,提问作者Readren

