AsynchronousFileChannel.write() 在对现有文件进行操作时会阻止主线程

AsynchronousFileChannel.write() blocks main thread while operating on existing file

提问人:Tony Af 提问时间:10/31/2023 最后编辑:Tony Af 更新时间:11/2/2023 访问量:76

问:

我注意到AsynchronousFileChannel类中“write”方法的奇怪行为。当函数写入新文件时,其行为是异步的,正如预期的那样。但是,当现有文件被覆盖时,函数调用会阻塞,直到写入完成。

我在带有 Java 19 和 Java 21 的 Windows OS 11 中注意到了这种行为。

下面是示例代码。首次运行代码时,“write”函数调用是非阻塞的。但是第二次运行,当存在“zeros.dat”时,“write”函数调用会阻止主线程。

有谁知道为什么会发生这种情况?

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        Path path = Paths.get("D:/zeros.dat");
        AsynchronousFileChannel ch = AsynchronousFileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
        ByteBuffer buffer = ByteBuffer.allocate(500000000);

        // Blocking behavior when overwriting a file
        ch.write(buffer, 0, null, new CompletionHandler<Integer, Void>() {
            @Override
            public void completed(Integer result, Void attachment) {
                System.out.println(result);
                System.out.println(Thread.currentThread().getName());
            }

            @Override
            public void failed(Throwable exc, Void attachment) {

            }
        });

        long i = 0;
        while (true){
            i++;
            System.out.println(i);
            Thread.sleep(500);
        }
    }
}

编辑1:当文件“zeros.dat”不存在时首次运行后的输出:

1
2
3
4
5
6
500000000
Thread-16
7
8
9
10
// and so on

当文件“zeros.dat”存在时,第二次运行后的输出:

// Main thread blocks here for aprox. 3 seconds. 
// HDD copies data during this period of time (see [picture 1][1]https://i.stack.imgur.com/H41ZA.png).
1
500000000
Thread-2
2
3
4
// and so on
Java 异步 NIO

评论

2赞 samabcde 10/31/2023
无法重现您的问题,您能否包括您看到的 1 的输出。文件不存在和 2.文件已存在?
0赞 Tony Af 11/2/2023
我已经使用控制台输出更新了问题。
2赞 user207421 11/2/2023
我建议不是写入,而是打开,花时间删除和重新分配大文件。甚至可能是巨大的缓冲区分配。这里没有证据表明这是写的。请提供更多细粒度的证据。
0赞 DuncG 11/2/2023
在写入之前添加更多 System.out.println 将有助于显示延迟是否确实在写入中。

答: 暂无答案