【问题标题】:Why use Java's AsynchronousFileChannel?为什么使用 Java AsynchronousFileChannel?
【发布时间】:2010-05-04 05:24:39
【问题描述】:

我可以理解为什么网络应用会使用多路复用(为了不创建太多线程),以及为什么程序会使用异步调用进行流水线处理(更高效)。但是我不明白 AsynchronousFileChannel 的效率目的。

有什么想法吗?

【问题讨论】:

    标签: java nio


    【解决方案1】:

    这是一个可用于异步读取文件的通道,即 I/O 操作在单独的线程上完成,因此您调用它的线程可以在 I/O 操作发生时执行其他操作.

    例如:类的read()方法返回一个Future对象来获取从文件中读取数据的结果。因此,您可以调用read(),它会立即返回一个Future 对象。在后台,另一个线程将从文件中读取实际数据。你自己的线程可以继续做事,当它需要读取数据时,你在Future对象上调用get()。然后将返回数据(如果后台线程尚未完成读取数据,它将使您的线程阻塞,直到数据准备好)。这样做的好处是您的线程不必等待整个读取操作的长度;在它真正需要数据之前,它可以做一些其他事情。

    the documentation

    请注意,AsynchronousFileChannel 将是 Java SE 7 中的一个新类,尚未发布。

    【讨论】:

    • 谢谢,但我想知道更多关于效率的原因——我不明白为什么在另一个线程中执行 I/O 会有所帮助。一个线程总是必须为 I/O 阻塞,并且没有报告资源重用、硬件功能利用率或任何其他性能增强行为。
    • 这样做的好处是您不需要需要两个线程。如果您正在写入大量数据,在低级别,磁盘控制器会在发出中断信号之前将数据写入磁盘,该中断会上升到堆栈以调用您的完成处理程序。你的线程在这个过程中不需要阻塞,可以做其他事情。你少用一个线程,线程调度器管理少一个线程,你不需要使用java notify。基本上,您使用更少的资源来做同样的事情。
    • 操作系统还可以优化异步IO操作的执行顺序。例如,如果您对文件中的多个区域进行大量异步读取,则操作系统可以将它们组合成顺序读取,而不必担心以给定的精确顺序执行所有操作。
    • @Ross,是什么阻止操作系统“分组”来自不同线程的并行请求?因此,分组并不能解释 2 倍的性能提升。
    • @Jesper,不使用等待 I/O 完成的线程肯定会节省一些资源,但我不相信一个什么都不做的额外线程(它也并行等待)会消耗一半的性能罗斯贾德森报道。
    【解决方案2】:

    我刚刚遇到了使用 AsynchronousFileChannel 的另一个有点出人意料的原因。在 NTFS 上跨大文件执行面向记录的随机写入(超过物理内存,因此缓存无济于事)时,我发现 AsynchronousFileChannel 在单线程模式下执行的操作是普通 FileChannel 的两倍多。

    我最好的猜测是,因为异步 io 归结为 Windows 7 中的重叠 IO,NTFS 文件系统驱动程序能够更快地更新其自己的内部结构,而不必在每次调用后创建同步点。

    我对 RandomAccessFile 进行了微基准测试,看看它的性能如何(结果非常接近 FileChannel,仍​​然是 AsynchronousFileChannel 性能的一半。

    不确定多线程写入会发生什么。这是在 Java 7 上的 SSD 上(SSD 比磁性快一个数量级,在适合内存的较小文件上快另一个数量级)。

    看看 Linux 上是否有相同的比率会很有趣。

    【讨论】:

    • 确实,只要您的文件短到适合操作系统缓存,无论您的 I/O 是否同步,I/O 都会立即完成。我只想补充一点,当您的 I/O 和计算长度相当时,您将获得最大 2 倍的加速。如果计算只是 I/O 时间的一小部分,或者 I/O 比计算短得可以忽略不计,那么重叠它们也没有意义。
    【解决方案3】:

    我能想到使用异步 IO 的主要原因是为了更好地利用处理器。想象一下,您有一些应用程序对文件进行某种处理。还假设您可以分块处理文件中包含的数据。如果您不使用异步 IO,那么您的应用程序可能会表现如下:

    1. 读取数据块。此时没有处理器利用率,因为您正在等待读取数据。
    2. 处理您刚刚读取的数据。此时,您的应用程序将在处理数据时开始消耗 CPU 周期。
    3. 如果要读取更多数据,请转到 #1。

    处理器利用率将上升,然后到零,然后上升,然后到零,...。理想情况下,如果您希望您的应用程序高效并尽可能快地处理数据,您不希望处于空闲状态。更好的方法是:

    1. 发出异步读取
    2. 读取完成后发出下一个异步读取,然后处理数据

    第一步是引导。您还没有数据,因此您必须发出读取。从那时起,当您收到读取已完成的通知时,您会发出另一个异步读取,然后处理数据。这样做的好处是,当您完成处理数据块时,下一次读取可能已经完成,因此您始终可以处理数据,从而更有效地使用处理器。如果您的处理在读取完成之前完成,您可能需要发出多个异步读取,以便您有更多数据要处理。

    尼克

    【讨论】:

      【解决方案4】:

      这是没有人提到的:

      一个普通的FileChannel 实现了InterruptibleChannel 所以它,以及任何使用它的东西,比如Files.newOutputStream() 返回的OutputStream,都有不幸的[1][2]interrupted state 的线程中对其执行任何阻塞操作(例如read()write())的行为将导致Channel 本身以java.nio.channels.ClosedByInterruptException 关闭。

      如果这是一个问题,使用AsynchronousFileChannel 是一个可能的替代方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-27
        • 1970-01-01
        • 1970-01-01
        • 2019-10-17
        • 2015-07-16
        • 2016-01-16
        • 2013-12-29
        • 2016-09-21
        相关资源
        最近更新 更多