2016-08-30 2 views
3

У нас есть метод GetDataParallel(), который может быть вызван многими клиентами в настоящее время, и мы используем ExecutorService для вызова MyCallable внутри него. Однако я обнаружил, что не вызвал бы execorService.shutdown(); приложение никогда не выходит, поэтому почему приложение не может выйти, мы должны закрыть все потоки пула потоков вручную до выхода приложения? и в среде обслуживания я думаю, что нам не нужно вызывать executorService.shutdown(); чтобы поддерживать приложение, верно?Когда исполнительService.shutdown(); следует называть

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class MultiThreading { 

    static ExecutorService executorService = Executors.newFixedThreadPool(100); 
    private List<String> _BusinessUnits= new ArrayList<String>(); 
    public static void main(String[] args) throws Exception { 

     MultiThreading kl =new MultiThreading(); 
     kl.GetDataParallel(); 
     Thread.sleep(10000); 
     System.out.println("111111111"); 
     //executorService.shutdown(); 

    } 

    public void GetDataParallel() throws Exception 
    { 
     _BusinessUnits.add("BU1"); 
     _BusinessUnits.add("BU2"); 
     _BusinessUnits.add("BU3"); 

     for(final String v : _BusinessUnits) 
     { 
      ExecutorServiceTest.executorService.submit(new MyCallable()); 
     } 
    } 
} 

    class MyCallable implements Callable { 
    @Override 
    public String call() throws Exception { 
     Thread.sleep(1000); 
     //return the thread name executing this callable task 
     System.out.println(Thread.currentThread().getName()); 
     return Thread.currentThread().getName(); 
    } 
} 
+0

Executor нити не демон нити и поэтому JVM не прекращается, пока они работают. Вы должны закрыть их вручную. http://stackoverflow.com/questions/2213340/what-is-daemon-thread-in-java Я предполагаю, что вы можете использовать пользовательский ThreadFactory, который обеспечивал потоки демона, но выключение намного проще и чище и безопаснее, особенно если вы делаете io в своих задачах. –

+0

@Jason C, что значит «во время работы»? run означает поток, выполняющий код, или они просто остаются в пуле потоков? Вы знаете, что закончившийся поток запускает вызываемый через 1 секунду, поэтому он не должен находиться в рабочем состоянии через 1 секунду, однако приложение по-прежнему не выходило через много раз. – Jason

+0

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

ответ

1

В Java есть два варианта нитей (конечно, в зависимости от того, как вы на них смотрите). «Пользовательские» потоки и потоки «Демон». Ваша заявка заканчивается в одном из следующих случаев:

  1. Вы называете System.exit()
  2. У вас нет User темы, оставленной в вашем приложении. Это объясняется here.

Обратите внимание, что ваша main функции выполняется с помощью виртуальной машины Java на нитке «User», что означает, что до тех пор, пока вы не закончили свою main функции. Большинство многопоточных приложений будут запускать основную функцию только для запуска всех необходимых потоков.

Идея потоков Daemon заключается в том, что вы можете что-то делать (регулярно), но если все остальные задачи выполняются, это не помешает выходу приложения.

По умолчанию новые потоки - это темы «Non Daemon», то же самое касается потоков, охваченных вашим ExecutorService. Если вы хотите изменить это, вы должны создать свой собственный ThreadFactory. A ThreadFactory позволяет вручную создавать потоки для вашего ExecutorService, он будет вызываться, когда ExecutorService нуждается в новом потоке. Вот пример, который создал «DAEMON» нитей:

public class DaemonThreadFactory implements ThreadFactory 
{ 

    @Override 
    public Thread newThread(final Runnable r) 
    { 
     Thread t = new Thread(r); 
     t.setDaemon(true); 
     return t; 
    } 
} 

Это может быть использовано при создании сервиса Исполнитель:

ExecutorService service = Executors.newFixedThreadPool(100, new DaemonThreadFactory()); 

Обратите внимание, что это также способ дать свои темы на заказ имена, которые очень полезны, так как многие logframeworks записывают имя потока (и показывает его отладчик).

Если вы должны были сделать это, в своем приложении он сразу же выйдет, потому что вы создаете потоки «Daemon», поэтому вам придется либо сохранить еще один поток в живых (это может быть сделано неявно другой структурой, например, если у вас есть GUI).

Альтернативой является вызов вручную System.exit(). Обычно вызывать код System.exit() в вашем коде не рекомендуется. В основном потому, что это не позволяет хорошо рефакторинг, повторное использование, тестирование и многие точки выхода делают ваше приложение непредсказуемым. Чтобы обойти эти проблемы, вы можете создать функцию обратного вызова, которая обрабатывает событие завершения задания. В этом приложении вы вызываете System.exit(), ваш тестовый код или другие приложения могут делать что-то еще.

+0

Если все 100 потоков зависают и не возвращаются, это означает, что ни один поток не может обслуживать новый запрос, в этом случае мы можем отменить зависание потока? Или, если есть тайм-аут, и после таймаута поток зависания будет выходить \ прерывать и автоматически возвращать пул потоков. Или есть другое хорошее предложение для решения проблем с потоками? – Jason

+0

@Jason Нет прекрасного способа прервать действительно застрявший поток (если вы хотите узнать больше, прочитайте, почему 'Thread.stop()' устарел).Вы можете сделать свои задачи прерывистыми (что означает, что они должны проверять флаг 'Thread.interrupted()' и обрабатывать 'InterruptedException' изящно. – Thirler

1

В среде приложения, необходимо вызвать остановку, чтобы обеспечить темы запускаемых ExecutorService должны остановить и он не должен принимать какие-либо более новые задачи. В противном случае JVM не выйдет.

В случае Службы вы должны позвонить shutdown перед остановкой выполнения Сервиса. Напр. в случае веб-приложения метод contextDestroyedServletContextListener полезен для вызова метода shutdownExecutorService. Это гарантирует, что любые существующие задачи должны быть завершены до внезапного завершения работы приложения, но никакая новая задача не будет принята для обработки.

+0

, В случае Сервиса пользовательский ThreadFactory с потоками демона или ExecutorService без потоков демона, какой из них более подходит? Или это полностью зависит от? – Jason

+0

ThreadFactory предназначен для того, чтобы избежать вызовов в «новую тему» ​​и загромождать код. Также полезно называть потоки и устанавливать приоритеты и т. Д. В случае службы используйте ExecutorService, когда вы хотите контролировать количество параллельных потоков (но их имя и приоритеты не имеют значения). – CuriousMind

4

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

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

В целом, рекомендуется создать четко определенную точку выхода, если она отсутствует в рамках фреймворка - она ​​позволяет вам иметь приложение, которое может быть выгружено (и, возможно, перезагружено с обновленной конфигурацией) без виртуальной машины, обязательно отключаясь вообще - я использовал этот трюк, чтобы иметь возможность перенастроить серверное приложение и перезагрузить с нулевым временем простоя в ответ на сигнал Unix.

Итак, простой ответ на ваш вопрос: «Когда он больше не будет использоваться». Почти в любом приложении есть точка, в которой это однозначно верно.

BTW, чтобы противоречить одному из других респондентов, ExecutorService может использовать потоки daemon - вы можете предоставить ThreadFactory, который настраивает потоки, но вы хотите, прежде чем запускать их. Но я бы посоветовал вам не использовать потоки демона и явно закрывать пул потоков в четко определенной точке - это не типичная практика, но это будет означать, что ваш код может быть полностью закрыт, и он будет побуждайте вас думать о жизненном цикле, что, вероятно, приведет к улучшению кода.

+1

Хорошо объяснил @Tim. – CuriousMind

+0

@ Тим, спасибо за ваше замечательное объяснение. – Jason

+0

«Если вы ожидаете, что ваше приложение выйдет, когда основной поток завершит все, что он должен сделать, вы хотите закрыть его, когда его работа будет выполнена« хорошо сказано :) –

1

Однако я нашел, если только я не вызвал исполнителяService.shutdown(); приложение никогда не выходит,

Думаю, нам не нужно вызывать executorService.shutdown(); чтобы поддерживать приложение, верно?

Да. Приложение сохраняется, если вы не позвонили executorService.shutdown()

Каждое приложение должно иметь точку выхода. Вы можете вызвать выключение во время этой точки выхода.

Если у вас нет известных точек выхода, ShutdownHook - это один из способов решения вашей проблемы. Но вы должны знать, что

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

Для примера вы процитировали, вы можете адресовать его несколько способов (invokeAll, Future.get(), CountDownLatch и др.). Посмотрите на соответствующий вопрос SE.

ExecutorService, how to wait for all tasks to finish

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