2010-09-23 4 views
6

Я пишу приложение, которое порождает несколько одновременных задач. Я использую пул потоков для реализации этого.Как остановить поток в threadpool

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

Моя проблема: Как остановить текущие задачи? Решением, которое я реализовал, является сохранение ссылки на поток задачи и вызов interrupt() в этой теме. В демо-коде:

public class Task implements Runnable { 

    private String name; 
    private Thread runThread; 

    public Task(String name) { 
     super(); 
     this.name = name; 
    } 

    @Override 
    public void run() { 
     runThread = Thread.currentThread(); 

     System.out.println("Starting thread " + name); 
     while (true) { 
      try { 
       Thread.sleep(4000); 
       System.out.println("Hello from thread " + name); 
      } catch (InterruptedException e) { 
       // We've been interrupted: no more messages. 
       return; 
      } 
     } 
    } 

    public void stop() { 
     runThread.interrupt(); 
    } 

    public String getName() { 
     return name; 
    } 
} 

И основным методом является:

public static void main(String args[]) { 
    executorService = Executors.newFixedThreadPool(2); 

    Task t1 = new Task("Task1"); 
    Task t2 = new Task("Task2"); 
    executorService.execute(t1); 
    executorService.execute(t2); 
    executorService.execute(new Task("Task3")); 
    executorService.execute(new Task("Task4")); 

    try { 
     Thread.sleep(12000); 
     t1.stop(); 
     System.err.println("Stopped thread " + t1.getName()); 
     Thread.sleep(8000); 
     t2.stop(); 
     System.err.println("Stopped thread " + t2.getName()); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

Это хорошее решение, или есть лучший способ, чтобы остановить запущенный поток в пул потоков?

+1

Вынуть вилку;) – sje397

ответ

2

В вашем переопределенном методе run() вы зацикливаетесь навсегда с while(true). Стандартное поведение должно состоять в том, чтобы иметь boolean runIndicator, который метод run() устанавливает значение true при его запуске, а затем ваш цикл должен быть while(runIndicator). Ваш метод stop() должен иметь простой набор runIndicator = false, поэтому следующая итерация цикла выпадет.

+0

В этой реализации метод 'stop()' также должен вызывать 'this.interrupt()'. В реальной реализации должен быть аналогичный метод, который вы можете использовать, чтобы остановить любой тяжелый подъем, вместо вызова 'Thread.sleep * (4000)'. Вы можете использовать очень похожий метод для 'runIndicator', чтобы выполнить это. –

+0

@Erick, 'interrupt()' не является методом runnable, поэтому это невозможно. 'Thread.currentThread(). Interrupt()' не будет работать либо потому, что вызов stop() происходит в другом потоке, чем тот, который вы хотите прервать. – Thirler

+1

Извините, я этого не понимал. Я всегда расширяю 'Thread' вместо' Runnable', поэтому у меня всегда есть доступ к этим вещам. Я бы сделал то же самое здесь. Это также решит проблему незначительного потокобезопасности, которую вы опубликовали в своем ответе. –

3

Идея вашего подхода - одно из нескольких правильных решений. Dealing with InterruptedException дает большой обзор того, как вы должны использовать механизм прерывания. Этот механизм в основном полезен при длительных вычислениях. Еще одна вещь, о которой нужно помнить, заключается в том, что другие библиотеки могут испортить ваш механизм прерывания, не делая того, что говорит гид (не перезагружая состояние прерывания, когда они не обрабатывали его и т. Д.).

Обратите внимание, что ваш класс Task не является потокобезопасным. Вы можете остановить задачу перед сохранением currentThread, что даст исключение NullPointerException.

Более простой подход заключается в установке переменной volatile booleanrunning и вместо while(true) петли делает while(running) подход (это, однако, намного более общий).

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

1

executorService.shutdown() и executorService.shutdownNow() следует использовать для отключения пула потоков для изящного выхода из приложения. См. ExecutorService.

См. Ответ Qwerky для завершения текущей текущей нити.

+4

Он хочет выключить нить, а не весь пул. –

+0

Этот ответ дается Кверки. Но отключение пула потоков также требуется для изящного выхода из приложения. –

2

Вы можете остановить его, удерживая ссылку на это будущее

  Future<?> future = exec.submit(new Runnable() { 
     while (true){ 
     try{ 
      obj.wait(); 
     }catch(InterruptedException e){ 
      System.out.println("interrupted"); 
      return; 
     } 
     }); 
     future.cancel(true); 

булевой для - может прервать при работе.

Я проверил и получил прерванное исключение из этой темы.

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

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