FileChannel#write 是否总是写入整个缓冲区?

2022-09-03 03:05:52

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

TL;DR

这会总是写入整个缓冲区吗...

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

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

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

?


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


仅供参考:文档说

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

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

和被覆盖方法的文档,WritableByteChannel#write(ByteBuffer)

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

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

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

除非另有指定,否则只有在写入所有 r 个请求的字节后,写入操作才会返回。某些类型的通道,根据其状态,可能只写入部分字节,或者可能根本不写入。例如,处于非阻塞模式的套接字通道不能写入的字节数超过套接字输出缓冲区中的可用字节数。

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

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

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


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

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

与此相反,写入方法的文档说

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

对我来说,这意味着 a 上的写入操作只会在写入所有字节后返回,因为在文档中没有以其他方式指定(除了返回值可能为 0 的语句,但这显然是来自重写方法的工件)FileChannel

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


答案 1

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

除非另有指定,否则只有在写入所有 r 个请求的字节后,写入操作才会返回。某些类型的通道,取决于它们的状态[1],可能只写入部分字节,或者可能根本不写入。

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

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

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

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

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


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


答案 2

推荐