使用Java NIO实现高效的读/写方法

时间:2012-08-17 01:57:02

标签: java nio

假设我们有SocketChannel(在非阻塞模式下)注册Selector以获取读取兴趣。让我们说在select()之后Selector告诉我们这个通道已准备好读取,我们有一些ByteBuffer。我们想要从我们的通道读取一些字节到这个缓冲区(ByteBuffer在读取之前被清除)。为此,我们使用channel的read()方法返回读取的实际字节数。让我们假设从通道读取后这个数字是正数,并且ByteBuffer的方法hasRemaining()返回true。在这种情况下,是否可以立即尝试从同一频道读取更多内容? write()的问题相同。如果write()返回正值而没有发送缓冲区的所有内容,那么在write()返回零之前立即重试是否切实可行?

2 个答案:

答案 0 :(得分:0)

这完全取决于数据到达的数据速率以及应用程序的延迟要求。如果你根本不关心延迟,你可能会通过延迟你的阅读兴趣来获得稍高的带宽,直到你怀疑有足够的数据到达你的缓冲区为止。

但是,你必须要小心。延迟读取可能会强制内核缓冲更多数据,可能填充其缓冲区,并且必须开始丢弃数据包或以其他方式进行某些流控制。这不仅会消除上一段中的任何好处。

所以一般来说,你想尽可能多地阅读,尽可能早。批量读取的好处至多是微不足道的,潜在的陷阱可能是主要的。请记住,您看到非完整读取的事实意味着您处理数据的速度比进入的速度快。换句话说,您处于一个CPU要刻录的状态,所以额外的较小读取的开销基本上是免费的。

答案 1 :(得分:0)

如果你得到一个简短的阅读结果,就没有更多的数据可以在没有阻止的情况下阅读,因此你必须不要再读一遍。否则,下一次读取几乎肯定会返回零或-1。

如果读取填充缓冲区,从一个连接的角度来看可能有意义继续读取直到它返回< = 0,但是你正在从其他通道窃取循环。你也需要考虑公平。通常,您应该执行一次读取并继续迭代所选键。如果有更多数据,那么select会告诉你下一次。

使用大缓冲区。

这也意味着在每次读取之前清除缓冲区是错误的。您应该通过翻转/获取/紧凑循环获取数据,然后缓冲区已准备好再次读取,您不会有丢失数据的风险。这反过来意味着每个连接需要一个缓冲区。