Java AsynchronousFileChannel-线程使用

ada*_*amM 6 java io asynchronous nio

我知道Java的AsynchronousFileChannel是异步api(不会阻塞调用线程),并且可以在系统线程池中使用线程。

我的问题是:AsynchronousFileChannel操作是否具有1:1的线程比率?

换句话说,如果循环使用AsynchronousFileChannel读取100个文件,它将使用100个线程来执行该操作还是仅使用少量线程(以标准NIO方式)?

sta*_*off 5

AsynchronousFileChannel一般使用的实现(例如在Linux上实际使用的实现)是SimpleAsynchronousFileChannelImpl,它基本上是提交Runnables阻塞IO读取+处理结果的线程(在同一线程中(填充Future或调用a CompletionHandler)),该线程ExecutorService作为AsynchronousFileChannel::open或作为参数提供否则,将使用默认的系统范围的默认值(这一个无限制的缓存线程池,但是具有一些可以配置的选项)。有人认为,最好使用文件,因为它们“总是可读”的,或者至少操作系统没有提供不知道的线索。

在Windows上,使用单独的实现,称为WindowsAsynchronousFileChannelImpl。在Windows Vista / 2008和更高版本(主版本> =“ 6”)上运行时,它使用I / O完成端口(也称为IOCP),并且通常表现得更像您期望的那样:默认情况下,它使用1个线程调度读取结果(可由"sun.nio.ch.internalThreadPoolSize"系统配置)属性)和用于处理的缓存线程池。

因此,回答您的问题:如果您不向提供自己的ExecutorService(例如固定的)AsynchronousFileChannel::open,那么它将是1:1的关系,因此100个文件的线程数为100;除了一个不古老的Windows,默认情况下将有1个线程处理I / O,但如果所有结果同时(不太可能但仍然)到达并且您使用CompletionHandlers,则它们也会在各自的线程中被调用。

编辑:我实现了100个文件的读取,并在Linux和Windows(openjdk8)上运行它,并且它1)确认两个类均实际使用了哪些类(为此删除,TF.class同时仍在命令行中指定它并查看stacktrace),2)某种程度的确认使用的线程数:Linux上为100,如果完成处理较快,则为Windows 4(如果CompletionHandlers不使用,则为相同); Windows上,如果完成处理较慢,则为100。如此丑陋,代码是:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.*;

public class AsynchFileChannelDemo {

    public static final AtomicInteger ai = new AtomicInteger();

    public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
        final List<ByteBuffer> bufs = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 100; i++) {
            Path p = Paths.get("some" + i + ".txt");
            final ByteBuffer buf = ByteBuffer.allocate(1000000);
            AsynchronousFileChannel ch = AsynchronousFileChannel.open(p, StandardOpenOption.READ);
            ch.read(buf, 0, buf, new CompletionHandler<Integer, ByteBuffer>() {
                @Override
                public void completed(Integer result, ByteBuffer attachment) {
                  bufs.add(buf);
                  // put Thread.sleep(10000) here to make it "long"
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {
                }
            });
        }
        if (args.length > 100) System.out.println(bufs); // never
        System.out.println(ai.get());
    }
}
Run Code Online (Sandbox Code Playgroud)

import java.util.concurrent.ThreadFactory;

public class TF implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        AsynchFileChannelDemo.ai.incrementAndGet();
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}
Run Code Online (Sandbox Code Playgroud)

编译这些文件,并将它们放在包含100个名为的文件的文件夹some0.txtsome99.txt,每个文件的大小为1Mb,以免读取速度太快,请按

java -Djava.nio.channels.DefaultThreadPool.threadFactory=TF AsynchFileChannelDemo
Run Code Online (Sandbox Code Playgroud)

打印的数字是线程工厂创建新线程的次数。

  • 不完全的。[当创建AsynchronousFileChannel时未指定线程池时,该通道将与系统相关的默认线程池相关联,该默认线程池可与其他通道共享。默认线程池由AsynchronousChannelGroup类定义的系统属性配置。](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html) (2认同)