FileChannel#write总是会写出整个缓冲区吗?

(这与(或者更确切地说是“相反”)有关。 如果有足够的数据,FileChannel.read会读取比指定的更少的字节吗? )

TL; DR

这总是会写出整个缓冲区……

ByteBuffer bytes = ...; fileOutputStream.getChannel().write(bytes); 

…或者是否有必要使用这样的循环:

 ByteBuffer bytes = ...; while (bytes.remaining() > 0) { fileOutputStream.getChannel().write(bytes); } 


由于另一个答案的评论 ,我想问一下,通过调用FileChannel#write(ByteBuffer)是否对将Buffer写入FileChannel的行为有任何保证。


仅供参考:文档说明

从给定缓冲区向该通道写入一个字节序列。

除非通道处于追加模式,否则从该通道的当前文件位置开始写入字节,在这种情况下,位置首先前进到文件的末尾。 如有必要,文件将生长以容纳写入的字节,然后使用实际写入的字节数更新文件位置。 否则,此方法的行为与WritableByteChannel接口指定的完全相同。

以及重写方法的文档, WritableByteChannel#write(ByteBuffer)

从给定缓冲区向该通道写入一个字节序列。

尝试向通道写入最多r个字节,其中r是缓冲区中剩余的字节数,即src.remaining(),此时调用此方法。

假设写入长度为n的字节序列,其中0 <= n <= r。 该字节序列将从索引p开始从缓冲区传输,其中p是调用此方法时缓冲区的位置; 写入的最后一个字节的索引将是p + n – 1.返回时,缓冲区的位置将等于p + n; 它的限制不会改变。

除非另有说明,否则只有在写入所有r请求的字节后才会返回写操作。 某些类型的通道(取决于它们的状态)可能只写入一些字节,或者根本不写。 例如,处于非阻塞模式的套接字通道不能再写入套接字输出缓冲区中可用的字节数。

可以随时调用此方法。 但是,如果另一个线程已经在此通道上启动了写操作,则此方法的调用将阻塞,直到第一个操作完成。

参数:src – 要从中检索字节的缓冲区

返回:写入的字节数,可能为零


在上面提到的关于从FileChannel 阅读的问题中,评论中有一些关于本文档的确切措辞和解释的讨论。 我认为文档中的关键区别在于对于read方法,文档说

读操作可能不会填充缓冲区,实际上它根本不会读取任何字节。

与此相反, write方法的文档说

除非另有说明,否则只有在写入所有r请求的字节后才会返回写操作。 某些类型的通道(取决于它们的状态)可能只写入一些字节,或者根本不写。

对我来说,这意味着FileChannel上的写操作只会在写完所有字节后返回,因为文档中没有另外指定(除了返回值可能为0的语句,但这显然是一个工件)从被覆盖的方法)

从我的文件大小高达80 MB(!)的测试中,写操作总是立即写入整个缓冲区。 但当然,这只是一个考验,并不足以作出深刻的陈述。 我试图在相关的OpenJDK类中跟踪调用,但这些调用很快就会分散到不同的本机实现中 – 毕竟,这不应该是必要的……

不,不能保证write()会耗尽整个缓冲区。 文档确实试图建立实现应该一次性写入所有字节的期望,但是它没有做出任何承诺:

除非另有说明否则只有在写入所有r请求的字节后才会返回写操作。 某些类型的通道,根据其状态[1] ,可能只写入一些字节,或者根本不

FileChannel.write()同样为不完整的写入留出了空间:

从给定缓冲区向该通道写入一个字节序列。

除非通道处于追加模式,否则从该通道的当前文件位置开始写入字节,在这种情况下,位置首先前进到文件的末尾。 如有必要,文件将生长以容纳写入的字节,然后使用实际写入的字节数更新文件位置。 否则,此方法的行为与WritableByteChannel接口指定的完全相同。

因此,虽然文本暗示完整写入是一般情况而不完整写入是例外,但它为未来(能够)遵守这种一般情况的替代/未来实现留下了空间。

正如您所指出的,这与read()的方法不同。 我想这是因为,在整合文档时,所有已知和预期的实现都遵循执行完整写入的一般情况。


[1]这可能是对非阻塞通道的引用。