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

时间:2008-10-06 15:31:58

标签: java sockets nio

我在阻止ObjectInputSteam上创建了ObjectOutputStreamSocketChannel,并尝试同时读写。我的代码是这样的:

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(...)

的性能优势

4 个答案:

答案 0 :(得分:4)

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

我测试了解决方法,但它确实有效。

答案 1 :(得分:4)

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

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);
}

答案 2 :(得分:2)

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

答案 3 :(得分:0)

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