2016-09-15 16 views
2

我理解Java的AsynchronousFileChannel是一个异步API(不会阻塞调用线程),并且可以使用系统线程池中的线程。Java AsynchronousFileChannel - 线程使用

我的问题是:do AsynchronousFileChannel操作有1:1的线程比率吗?换句话说,如果一个循环使用AsynchronousFileChannel读取100个文件,它会使用100个线程来完成,还是只使用少量的线程(以标准NIO方式)?通常使用(和实际使用例如在Linux)

回答

1

AsynchronousFileChannel实现是SimpleAsynchronousFileChannelImpl基本上提交Runnables那些阻塞IO读取+处理结果在同一个线程(或者填充未来或调用一个CompletionHandler)到ExecutorService其或者作为参数提供给AsynchronousFileChannel::open,否则使用默认系统范围的一个(其中is是无限制的缓存线程池,但有一些选项can be configured)。一些think,它是最好的,可以做文件,因为他们是“永远可读”或至少操作系统不提供任何线索,他们不是。

在Windows上使用一个单独的实现,它被称为WindowsAsynchronousFileChannelImpl。它在Windows Vista/2008及更高版本(主要版本> =“6”)上运行时使用IOCP(也称为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,同时仍然在命令行中指定它, 2)如果完成处理速度快(如果不使用CompletionHandlers,那么它将是相同的),如果完成处理速度慢,则在Linux上为100,在Linux上为4。丑陋的,因为它是,代码:

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()); 
    } 
} 

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; 
    } 
} 

编译这些,把它们与100个文件名为some0.txtsome99.txt,每个1Mb的大小,使阅读ISN”的文件夹中t太快,运行它为

java -Djava.nio.channels.DefaultThreadPool.threadFactory=TF AsynchFileChannelDemo 

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

+2

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

+0

@EJP,感谢您的反馈。我肯定会编辑我的答案,或者如果它超出了恢复范围,请将其删除,但我首先要更好地理解它。据我所知,这个线程池被创建[在这里](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/nio/ch/ThreadPool。 java#l95),因此它使用“threadFactory”和“initialSize”设置,但它仍然是一个无限制的缓存线程池。我是否应该更清楚地表明它不是在我的答案中以每个频道为基础创建的?或者我的回答有其他问题吗? – starikoff

+0

共享默认线程池的概念似乎指向非1:1关系的方向:即使用NIO魔术,可以使用固定的较少数量的线程同时读取多个文件。如果它不这样做,那么我不知道在标准java.ion中使用这个好处,并将执行包装在一个线程中并返回未来。 – adamM