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

Mar*_*o13 16 java filechannel

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

TL; DR:

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

ByteBuffer bytes = ...;
fileOutputStream.getChannel().write(bytes);
Run Code Online (Sandbox Code Playgroud)

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

ByteBuffer bytes = ...;
while (bytes.remaining() > 0)
{
    fileOutputStream.getChannel().write(bytes);
}
Run Code Online (Sandbox Code Playgroud)


由于在另一个答案的评论,我想问一下是否有相关的写的行为的任何担保BufferFileChannel调用FileChannel#write(ByteBuffer).


仅供参考:文档说明

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

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

以及被覆盖方法的文档WritableByteChannel#write(ByteBuffer)

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

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

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

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

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

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

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


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

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

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

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

对我来说,这意味着在一个写操作FileChannel 所有字节都被写入后只能返回,因为没有在文档中另有规定它(除语句返回值可能为0,但是这显然是从一个神器被覆盖的方法)

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

小智 2

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

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

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

将给定缓冲区中的字节序列写入此通道。

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

因此,虽然文本暗示完整写入是一般情况,不完整写入是例外情况,但它为可能无法(能够)遵守这种一般情况的替代/未来实现敞开了大门。

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


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