问题描述
我知道Java的AsynchronousFileChannel是异步api(不会阻塞调用线程),并且可以在系统线程池中使用线程.
我的问题是:AsynchronousFileChannel操作是否具有1:1的线程比率?
换句话说,如果循环使用AsynchronousFileChannel读取100个文件,它将使用100个线程来执行该操作还是仅使用少量线程(以标准NIO方式)?
AsynchronousFileChannel
实现(通常在Linux上实际使用)是 SimpleAsynchronousFileChannelImpl 基本上会提交Runnables
来阻止IO读取和处理导致到ExecutorService
的同一线程(填充未来或调用CompletionHandler
)作为 AsynchronousFileChannel::open
,否则将使用系统范围内的默认值(是一个无限缓存的线程池,但是具有可以配置).一些认为认为最好使用文件,因为它们总是可读的" ",或者至少操作系统没有提供任何提示.
在Windows上使用单独的实现,称为 WindowsAsynchronousFileChannelImpl .它使用 I/O完成端口在Windows Vista/2008和更高版本(主版本> ="6")上运行时又称为IOCP,通常表现得更像您期望的那样:默认情况下,它使用1个线程来调度读取结果(可通过"sun.nio.ch.internalThreadPoolSize"
系统属性配置) )和用于处理的缓存线程池.
因此,回答您的问题:如果您不向AsynchronousFileChannel::open
提供自己的ExecutorService
(例如固定的),那么这将是1:1的关系,因此100个文件的100个线程;除了一个非古老的Windows,默认情况下将有1个线程处理I/O,但是如果所有结果同时(不太可能但仍然同时)到达并且您使用CompletionHandlers
,它们也会在各自的线程中被调用. /p>
我实现了读取100个文件的操作,并在Linux和Windows(openjdk8)上运行了该文件,它1)确认了两者都实际使用了哪些类(为此删除TF.class
,同时仍指定在命令行中查看堆栈跟踪),2)确认使用的线程数:Linux上为100,Windows上为4(如果完成处理速度很快)(如果不使用CompletionHandlers
,则相同),100在Windows上,如果完成处理缓慢.如此丑陋,代码是:
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.txt
至some99.txt
的文件的文件夹中,每个文件的大小为1Mb,以免读取不太快,请按以下方式运行
java -Djava.nio.channels.DefaultThreadPool.threadFactory=TF AsynchFileChannelDemo
打印的数字是线程工厂创建新线程的次数.
I understand Java's AsynchronousFileChannel to be an async api (does not block the calling thread) and can use a thread in a system thread pool.
My question is: do AsynchronousFileChannel operations have a 1:1 thread ratio?
In other words, if a loop use AsynchronousFileChannel to read 100 of files, will it use 100 threads to do that or will it use only a small number of threads (in standard NIO fashion)?
AsynchronousFileChannel
implementation used in general (and actually used e.g. on Linux) is SimpleAsynchronousFileChannelImpl which basically submits Runnables
that do blocking IO read + process result in the same thread (either fill a future or call a CompletionHandler
) to an ExecutorService
which either is supplied as an argument to AsynchronousFileChannel::open
, or else a default system-wide one is used (which is an unbounded cached thread pool, but has some options that can be configured). Some think that it is the best that can be done with files since they are "always readable" or at least the OS doesn't provide any clue that they aren't.
On Windows a separate implementation is used which is called WindowsAsynchronousFileChannelImpl. It uses I/O completion ports a.k.a IOCP when run on Windows Vista/2008 and later (major version >= "6") and generally behaves more like you would expect: by default it uses 1 thread to dispatch read results (configurable by "sun.nio.ch.internalThreadPoolSize"
system property) and a cached thread pool for processing.
So, answering your question: if you don't supply your own ExecutorService
(say a fixed one) to AsynchronousFileChannel::open
, then it will be a 1:1 relationship, so there will be 100 threads for 100 files; except for a non-ancient Windows, where by default there will be 1 thread handling I/O but if all results arrive simultaneously (unlikely but still) and you use CompletionHandlers
, they will be called each in its own thread too.
Edit: I implemented reading of 100 files and ran it on Linux and Windows (openjdk8) and it 1) confirms which classes are actually used on both (for that remove TF.class
while still specifying it in a command line and see the stacktrace), 2) sort of confirms the number of threads used: 100 on Linux, 4 on Windows if completion processing is fast (it will be the same if CompletionHandlers
are not used), 100 on Windows if completion processing is slow. Ugly as it is, the code is:
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());
}
}
and
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;
}
}
Compile these, put them in a folder with 100 files named some0.txt
to some99.txt
, each being 1Mb in size so that reading isn't too fast, run it as
java -Djava.nio.channels.DefaultThreadPool.threadFactory=TF AsynchFileChannelDemo
The number printed is the number of times a new thread was created by the thread factory.
这篇关于Java AsynchronousFileChannel-线程使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!