Java中AsynchronousFileChannel不是真正的异步


从程序员的角度来看,异步文件 IO 允许在不阻塞调用执行线程的情况下从文件系统读/写数据。也就是说,请求操作的线程。

程序员一直可以做到这一点。只需生成一个负责执行 IO 操作的新线程,并允许使用某种机制将结果传达给其他线程(如未来线程)即可。

但是,JDK 通过 java.nio.channels.AsynchronousFileChannel 提供了一个开箱即用的 API。使用它的理由是,某些操作系统可能会为异步 IO 提供本机支持,即内核本身可以理解并促进异步文件操作。

但是:

  • 在 Linux 上,AsynchronousFileChannel 只是将阻塞的 IO 操作转移到执行器服务上。没有本机支持。
  • 与同步 FileChannel 相比,如果提交写入的速度快于 IO 操作的完成速度,则更容易耗尽可用堆内存。
  • 操作系统(OS)在说谎。请注意,在较低层次上,操作系统也提出了异步的概念。一般来说,操作系统在返回 IO 调用时,只是以透明方式将数据写入内存缓冲区。只有在此之后,它们才会真正持续写入物理磁盘。


门面背后
AsynchronousFileChannel 能使您的应用程序反应更快。如果应用程序是面向用户的,代码可以在读写完成前返回给用户,这样用户就可以做其他事情了。

但我们为什么需要特殊功能呢?为什么不直接在不同的线程中进行阻塞读/写操作,并使用未来作为操作完成后返回 "信号 "的机制呢?

原因是 AsynchronousFileChannel 提供了一个门面,在这个门面后面,一些平台可以使用不需要线程的本地操作系统功能,因此更具可扩展性--因为线程是有成本的。

API
让我们先看看如何使用它。我们的重点是文件 IO 的并发方面,因此我们只对 1) 打开文件、2) 读取或写入文件以及 3) 关闭文件感兴趣。异步文件通道(AsynchronousFileChannel)还有其他功能,比如锁定文件,我们将忽略这些功能。

详细点击标题

测试结果:
测试持续时间随着文件的大小大致线性增长。这证实了异步文件通道只是将 IO 操作转移到另一个执行线程上,这可能令大多数人感到惊讶。它没有任何“本机”支持。

重要的是,写入后关闭打开的文件是一个相对较快的操作。由于操作系统在向调用 JVM 应用程序报告 IO 操作已完成之前正在写入物理磁盘,因此在关闭文件时,没有任何内容需要写入。

总结:
在 Linux 上,Java 的 AsynchronousFileChannel 不支持真正的异步文件 IO。相反,它会将同步 IO 操作发送到你作为参数传递的公共线程池上。

异步有两个来源。一个是本文的主要目标 AsynchronousFileChannel。另一个原因是操作系统先写入内存缓冲区,然后(在返回应用程序调用后)才与物理设备同步。这些机制是独立的,但你可以打开一个文件通道,指示操作系统绕过这些缓冲区。