2010-02-24 6 views
49

Я всегда читал, что создание потоков дорого.
Я также знаю, что вы не можете повторить нить.java thread reuse

Я вижу в ИХ Executors класса:

Создает пул потоков, который создает новые темы, по мере необходимости, но будет повторно использовать ранее построенные темы, если они доступны.

Помните слово «повторное использование».

Как использовать потоки пулов для повторного использования?

ответ

44

Я думаю, что я понял, что confuzzabling ты так вот мой развернутый ответ: терминология крошечное немного вводит в заблуждение (очевидно, иначе вы бы не задать этот вопрос конкретно поставить акцент на «повторного использования»):

Как использовать потоки пулов для повторного использования?

То, что происходит в том, что один поток может быть использован для обработки нескольких задач (как правило, передаются в Runnable, но это зависит от вашей структуры «исполнитель»: исполнители по умолчанию принимает Runnable, но вы можете написать свой собственный «исполнитель "/ thread-pool принимает что-то более сложное, чем Runnable [как, скажем, CancellableRunnable]).

Теперь в реализации по умолчанию ExecutorService, если поток каким-то образом завершен во время использования, он автоматически заменяется новым потоком, но это не «повторное использование», о котором они говорят. В этом случае «повторного использования» нет.

Так это правда, что вы не можете назвать start() на Java тему дважды но вы можете пройти столько Runnable, как вы хотите исполнителя и каждый run() метод Runnable «s созываются один раз.

Вы можете передать 30 Runnable на 5 Java Thread и каждый рабочий поток может быть вызовом, например, run() 6 раз (практически там не гарантирует, что вы будете выполнения ровно 6 Runnable за Thread но это детали).

В этом примере start() было бы названо 6 раз. Каждый из них 6 start() будут называть ровно один разrun() метода каждого Thread:

От Thread.start() Javadoc:

* Causes this thread to begin execution; the Java Virtual Machine 
* calls the <code>run</code> method of this thread. 

НО затем внутри run() метода каждого потока Runnable должны быть удалены из очереди и будет вызываться метод run() каждого из Runnable. Поэтому каждый поток может обрабатывать несколько Runnable. Это то, что они называют «повторным использованием потока».

Один из способов сделать свой собственный пул потоков, чтобы использовать очередь блокировки, на которую вы епдиеее runnables и имеете каждый из вашего потока после того, как это делается обработкой run() метода Runnable, Dequeue следующего Runnable (или блока) и запустите его метод run(), затем промойте и повторите.

Я думаю, часть путаницы (и это немного сбивает с толку) исходит из того, что Thread принимает Runnable и при вызове start()run() метод Runnable «s называется в то время как резьба по умолчанию бассейны в также взять Runnable ,

+22

tl; dr Потоки пула потоков - это в основном запущенные петли, которые вытягивают заданные задачи из очереди.Нити не прекращают выполнение, когда они обслуживают задачу, они просто ждут следующего, который будет отправлен в очередь. Они никогда не получают «повторение», как задавали в вопросе, поскольку они просто постоянно работают. – Sogger

4

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

Для получения более подробной информации смотрите на documentation.

-4

Если thread закончен, обязательно его можно повторно использовать повторно. Вы можете убедиться, что он не работает, вызвав isAlive() или что-то подобное.

EDIT: Если кто-то приостановил thread, нельзя начинать снова. Я не понимаю, почему он не может быть запущен, если он закончил нормально. Но я даю пользу сомнению и говорю , что его нельзя использовать повторно.

+4

@fastcodejava: Если поток завершен, вы не можете снова вызвать * start() *, что даст вам * IllegalThreadStateException *. – SyntaxT3rr0r

12

Метод потоков в пуле потоков состоит не только из одной задачи. Метод потока run потока в пуле потоков содержит цикл. Он вытаскивает задачу из очереди, выполняет задачу (которая возвращает обратно в цикл, когда она завершена), а затем получает следующую задачу. Метод run не завершается, пока поток больше не нужен.

Edited добавить:

Вот run метод Worker внутреннего класса в ThreadPoolExecutor.

696:   /** 
697:   * Main run loop 
698:   */ 
699:   public void run() { 
700:    try { 
701:     Runnable task = firstTask; 
702:     firstTask = null; 
703:     while (task != null || (task = getTask()) != null) { 
704:      runTask(task); 
705:      task = null; // unnecessary but can help GC 
706:     } 
707:    } finally { 
708:     workerDone(this); 
709:    } 
710:   } 
+0

@ Майк Дэниелс: точно ... Это сказал, вам нужно, чтобы этот код Солнца назначал * «task = null;» *, а затем комментарии * «// ненужные, но могут помочь GC» *. Вы бы разместили кусок кода здесь, когда делали это так, и вы прыгнули бы на горло, если бы вы сказали, что это бессмысленно;) – SyntaxT3rr0r

+5

@Wizard: Это на самом деле не нужно - (странный) тест для задачи! = null в цикле делает необходимым предотвратить непрерывную обработку одной и той же задачи. Даже если цикл был более обычной задачей обнуления, было бы хорошо, потому что иначе, если getTask() блокирует в течение длительного времени, GC задачи в противном случае задерживается на один и тот же отрезок времени. –

+0

@Software Monkey: очень проницательный о второй точке. 'getTask', вероятно, выйдет из очереди блокировки с тайм-аутом. Это может блокироваться на всю длину таймаута. Если в задаче имеется много полей (для переданных параметров для задачи), то более ранний GC был бы полезен. –

0

Пул потоков создает собственные потоки и поставляет свои умные маленькие Runnables для этих потоков. Эти Runnables никогда не заканчиваются, а синхронизируются в очереди (они ждут()), пока в этой очереди не появится Callable; они уведомляются, когда это происходит, и их Runnable запускает Callable из очереди, и весь сценарий снова повторяется.