2016-09-15 2 views
2

Я понимаю, что AsynchronousFileChannel Java является асинхронным api (не блокирует вызывающий поток) и может использовать поток в пуле системных потоков.Java AsynchronousFileChannel - использование потоков

Мой вопрос: do Асинхронные операцииФинансовые операции имеют соотношение потоков 1: 1?

Другими словами, если цикл использует AsynchronousFileChannel для чтения 100 файлов, будет ли он использовать 100 потоков для этого или будет использовать только небольшое количество потоков (в стандартном режиме NIO)?

ответ

1

AsynchronousFileChannel реализация используется в целом (и фактически используемых, например, в Linux) является SimpleAsynchronousFileChannelImpl, который в основном представляет Runnables, которые блокирование ввода-вывода для чтения + результат процесса в том же потоке (либо заполнить будущее или вызвать CompletionHandler) к ExecutorService, которые либо поставляется в качестве аргумента AsynchronousFileChannel::open, или же используется системная система по умолчанию (которая содержит is неограниченный пул кешированных потоков, но имеет некоторые параметры, которые can be configured). Некоторые think, что это лучшее, что можно сделать с файлами, так как они «всегда читаемы» или, по крайней мере, ОС не дает никакой подсказки, которой они не являются.

В Windows используется отдельная реализация, которая называется WindowsAsynchronousFileChannelImpl. Он использует I/O completion ports aka IOCP при работе в Windows Vista/2008 и более поздних версиях (основная версия> = «6») и, как правило, ведет себя так же, как и следовало ожидать: по умолчанию он использует 1 поток для отправки результатов чтения (настраивается по системному свойству "sun.nio.ch.internalThreadPoolSize") и пул кэшированных потоков для обработки.

Так, отвечая на ваш вопрос: если вы не предоставите свой собственный ExecutorService (скажем, фиксированный один), чтобы AsynchronousFileChannel::open, то это будет 1: 1 отношения, так что будет 100 нитей для 100 файлов; за исключением не древней Windows, где по умолчанию будет задействован один поток обработки ввода-вывода, но если все результаты придут одновременно (маловероятно, но все же), и вы используете CompletionHandlers, они также будут вызываться каждый в своем потоке.

Edit: Я осуществил чтение 100 файлов и запустил его на Linux и Windows (openjdk8) и 1) подтверждает, какие классы фактически используются на обоих (для этого удалить TF.class при этом указав его в командной строке и см. stacktrace), 2) тип подтверждения количества используемых потоков: 100 в Linux, 4 в Windows, если обработка завершения выполняется быстро (это будет то же самое, если 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, каждый из которых 1Мб по размеру, так что чтение 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

Смежные вопросы