Java:通过Object(In | Out)putStreams阻塞SocketChannel上是否可以进行并发读写?

我在阻塞的SocketChannel上创建了一个ObjectInputSteamObjectOutputStream ,并尝试同时读写。 我的代码是这样的:

 socketChannel = SocketChannel.open(destNode); objectOutputStream = new ObjectOutputStream(Channels.newOutputStream(socketChannel)); objectInputStream = new ObjectInputStream(Channels.newInputStream(socketChannel)); Thread replyThread = new Thread("SendRunnable-ReplyThread") { @Override public void run() { try { byte reply = objectInputStream.readByte();//(A) //..process reply } catch (Throwable e) { logger.warn("Problem reading receive reply.", e); } } }; replyThread.start(); objectOutputStream.writeObject(someObject);//(B) //..more writing 

问题是在行(B)块处写入,直到在行(A)处的读取完成(由SelectableChannel#blockingLock()返回的对象上的块)。 但app逻辑规定在完成所有写操作之前读取不会完成,因此我们有一个有效的死锁。

SocketChannel javadocs表示支持并发读写。

当我尝试使用常规Socket解决方案时,我没有遇到过这样的问题:

 Socket socket = new Socket(); socket.connect(destNode); final OutputStream outputStream = socket.getOutputStream(); objectOutputStream = new ObjectOutputStream(outputStream); objectInputStream = new ObjectInputStream(socket.getInputStream()); 

但是,我无法利用FileChannel#transferTo(...)的性能优势

这似乎是java.nio.channels.Channels一个错误(感谢Tom Hawtin;下次将其作为答案发布)。 这里描述了一个很好的描述和解决方法(实际上是Tom列出的bug的副本):

我测试了变通方法并且它有效。

错误报告中的解决方法对我有用。 值得注意的是,只有一个输入或输出需要被包装以使变通工作正常 – 因此如果性能在一个方向上特别重要,那么您可以包装不太重要的一个并确保另一个将获得所有可用的优化它。

 public InputStream getInputStream() throws IOException { return Channels.newInputStream(new ReadableByteChannel() { public int read(ByteBuffer dst) throws IOException { return socketChannel.read(dst); } public void close() throws IOException { socketChannel.close(); } public boolean isOpen() { return socketChannel.isOpen(); } }); } public OutputStream getOutputStream() throws IOException { return Channels.newOutputStream(socketChannel); } 

如果你想与SocketChannel同时使用InputStream和OutputStream,从查看源代码,你需要调用SocketChannel.socket()并使用行为略有不同的流。

有趣的bug! 你说虽然你不能使用FileChannel#transferTo。 在传递给FileChannel#transferTo之前,如何使用Channesl#newChannel将非NIO套接字的I / O流封装到通道中?