2014-11-14 1 views
14

У меня есть основной вопрос о том, как ExecutorService работает на Java.ExecutorService vs Casual Thread Spawner

Сложно видеть разницу между просто созданием Threads для выполнения некоторых задач параллельно и назначения каждой задачи на ThreadPool.

ExecutorService также выглядит очень простым и эффективным в использовании, поэтому мне было интересно, почему мы не используем его все время.

Это просто вопрос, выполняющий свою работу быстрее, чем другой?

Вот два очень простых примеров, чтобы показать различие между этими двумя способами:

Использование ИСПОЛНИТЕЛЬ службы: Hello World (задача)

static class HelloTask implements Runnable { 

String msg; 

public HelloTask(String msg) { 
this.msg = msg; } 
public void run() { 
long id = Thread.currentThread().getId(); 
    System.out.println(msg + " from thread:" + id); 
    } 
} 

Использование исполнителем услуг: Hello World (создание исполнителя , подчиняясь)

static class HelloTask { 
public static void main(String[] args){ 

int ntasks = 1000; 
ExecutorService exs = Executors.newFixedThreadPool(4); 

for (int i=0; i<ntasks; i++) { HelloTask t = 
new HelloTask("Hello from task " + i); exs.submit(t); 
} 
exs.shutdown();}} 

следующее показано аналогичным примером, но расширение интерфейса Callable, не могли бы вы рассказать мне разницу между этими двумя и в каких случаях следует использовать конкретную, а не другую?

Использование ИСПОЛНИТЕЛЬ службы: Счетчик (задачи)

static class HelloTaskRet implements Callable<Long> { 

String msg; 

public HelloTaskRet(String msg) { 
this.msg = msg; } 

public Long call() { 
long tid = Thread.currentThread().getId(); 
System.out.println(msg + " from thread:" + tid); 
return tid; 
    } } 

Использование ИСПОЛНИТЕЛЬ службы: (создание, направляющее)

static class HelloTaskRet { 

public static void main(String[] args){ 
int ntasks = 1000; 
ExecutorService exs = Executors.newFixedThreadPool(4); 

Future<Long>[] futures = (Future<Long>[]) new Future[ntasks]; 

for (int i=0; i<ntasks; i++) { HelloTaskRet t = 
new HelloTaskRet("Hello from task " + i); futures[i] = exs.submit(t); 
} 
exs.shutdown(); 

}} 
+0

Оба примера используют 'ExecutorService' вместо создания новых потоков, поэтому я не уверен, что вы сравниваете между двумя примерами в этом случае. Ваше замешательство в том, когда использовать «Runnable» и когда использовать «Callable»? –

ответ

13

Хотя вопрос и образец кода не коррелируют, Я попробую уточнить и то, и другое. Преимущество ExecutorService над беспорядочно порождающими потоками заключается в том, что он ведет себя предсказуемо и позволяет избежать накладных расходов на создание потоков, которое относительно велико для JVM (например, для резервирования памяти для каждого потока). По предсказуемости, по крайней мере для fixedThreadPool, я имею в виду, что вы знаете максимальное количество параллельных потоков, и знаете, когда и как они могут быть созданы (поэтому ваша JVM не будет взорваться в случае внезапных пиков).

Винсом Emigh: ExecutorService также поддерживает cachedThreadPool, которые не имеют макс. Основная причина, по которой люди предпочитают использовать ExecutorService, составляет , чтобы предотвратить накладные расходы при создании нескольких потоков (используя рабочий темы). Он в основном используется в случаях, когда многие небольшие задачи должны быть , выполненные в отдельном потоке. Также, не забудьте о singleThreadExecutor.

Теперь по теме Runnable против Callable, легко видеть из ваших примеров. Callable s может вернуть держатель стоимости (Future), который в конечном итоге будет заселен фактическим значением в будущем. Runnable s ничего не может вернуть.

Винсом Emigh: Runnable также не может бросить исключения, в то время как Callable может.

+0

Нет проблем. Вы должны отредактировать его в своем ответе, чтобы я мог удалить комментарий –

6

ExecutorService дает много преимуществ по сравнению с простыми нитями

  1. Вы можете создавать/управлять/управления жизненным циклом Темы & оптимизировать стоимость создания потока накладных расходов
  2. Вы можете обработка управления задач (Работа кражи, ForkJoinPool, invokeAll) и т. Д.
  3. Вы можете запланировать задачи в Будущее время
  4. Вы можете следить за прогрессом и здоровье потоков

Даже для одной нити, я предпочитаю использовать Executors.newFixedThreadPool(1);

Посмотрите на смежных SE вопросы:

Java's Fork/Join vs ExecutorService - when to use which?

What are the advantages of using an ExecutorService?