FileChannel#write会始终写入整个缓冲区吗?

17
这与如果有足够的数据,FileChannel.read是否会读取少于指定数量的字节?相关(或者说是“相反”的)。 TL;DR

这是否总是写入整个缓冲区...

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

...或者需要像这样使用循环吗:
ByteBuffer bytes = ...;
while (bytes.remaining() > 0)
{
    fileOutputStream.getChannel().write(bytes);
}

?


由于另一个答案中的评论,我想问一下是否有关于通过调用{{link2: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类中的调用,但这些很快就分散到不同的本地实现中 - 毕竟,这并不是必要的...
1个回答

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

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

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

从给定的缓冲区向此通道写入一系列字节。

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

因此,虽然文本暗示完全写入是一般情况,而不完整的写入是例外情况,但它为可能无法(能够)遵守这种一般情况的其他/未来实现留下了余地。
正如您所指出的,这与read()的方法不同。我想这是因为在整理文档时,所有已知和预期的实现都遵守了执行完全写入的一般情况。

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


1
谢谢,但这基本上就是我在问题中引用的内容。我认为关键点在于它说:“除非另有规定,否则写操作将仅在写入所有请求的字节后返回”在一般情况下,对于FileChannel情况,它并没有明确说明它可能在写入所有请求的字节之前返回。 (是的,我看到返回值被称为“实际写入的字节数”,但它并没有说这可能少于请求的数量)。如果怀疑,一个人可能需要假设它不会写入所有字节。 - Marco13
1
@Marco13 我们都引用了文档,答案就在里面。 :) 我强调了指向不完整写入可能性的部分。正如你所说,你不能确定,所以你必须考虑到这种可能性。 - JvR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接