2016-03-17 3 views
1

Я пытаюсь написать программу, которая будет делить работу среди нескольких рабочих потоков java. Проблема в том, что когда я запускаю его из командной строки, он никогда не возвращается. Я не возвращаю свое приглашение и, в конце концов, должен закрыть ctrl-c.Приложение Java, которое использует ExecutorService, никогда не закрывается

я упростил его к следующему тривиальному случаю

import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 

public class TestExeServ { 

    private class ExpensiveTask implements Callable<Integer>{ 
     private final String msg; 
     public ExpensiveTask(String str){ 
      this.msg = str; 
     } 

     @Override 
     public Integer call() { 
      System.out.println("My message was " + msg); 
      return 1; 
     } 
    } 

    private void run() 
    { 
     final ExecutorService exeServ = Executors.newFixedThreadPool(2); 
     Future<Integer> result = exeServ.submit(new ExpensiveTask("Hello!")); 
     try { 
      System.out.println(" Task is done, it returned " + result.get()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String args[]) 
    { 
     System.out.println("Start");  

     TestExeServ tes = new TestExeServ(); 
     tes.run(); 
     System.out.println("Done"); 
    }  
} 

Выход этой программы

[email protected]:/local/mnt/workspace/TestExeServ/bin$ java TestExeServ 
    Start 
    my message was Hello! 
    Task is done, it returned 1 
    done 

И это все. Он там висит. Нет подсказки. Если я удалю строку ExecutorService.submit, я получаю

[email protected]:/local/mnt/workspace/TestExeServ/bin$ java TestExeServ 
    Start 
    done 
    [email protected]:/local/mnt/workspace/TestExeServ/bin$ 

Программа закрывается естественным образом.

Есть ли какая-то задача очистки, которую я должен выполнять в ExecutorService, что я не делаю правильно? Я предположил, что вызов .get() соединяет потоки. Разве это не так?

+2

Вам необходимо выключить Executor –

+1

Или использовать фабрику нитей, которая создает потоки демона. – assylias

+2

или установить флаг allowCoreThreadTimeout [приложение ThreadPoolExecutor не завершено] (http://stackoverflow.com/q/30633698/217324) –

ответ

5

Вы должны позвонить ExecutorService#shutdown() или ExecutorService#shutdownNow(), чтобы прервать пул исполнителей исполнителя. В противном случае потоки будут оставаться активными, тем самым предотвращая прерывание JVM.

Из ExecutorService класса Javadoc:

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

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


Как @mre говорится в комментарии, я сделаю это явно - вы смело можете просто позвонить exeServ.shutdown() в конце вашего метода run() так Future.get() будет блокировать, поэтому, когда выполнение кода после get() вас не нужно исполнителю больше. Однако, если это упрощенная версия более сложного реального сценария, вам может потребоваться найти нужное место для безопасного вызова shutdown().

+0

И 'shutdownNow' в порядке, потому что' result.get' будет блокироваться до завершения задачи, иначе вы рискуете отменить его. – mre

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

  • Нет связанных вопросов^_^