2010-09-23 12 views
6

Piszę aplikację, która spawnuje wiele równoczesnych zadań. Używam puli wątków, aby to zaimplementować.jak zatrzymać wątek w wątku Pula

Może się zdarzyć, że wystąpi zdarzenie, które powoduje, że obliczenia wykonywane w zadaniach są nieprawidłowe. W takim przypadku chciałbym zatrzymać aktualnie uruchomione zadania i rozpocząć nowe.

Mój problem: Jak zatrzymać aktualnie wykonywane zadania? Rozwiązaniem, które zaimplementowałem, jest przechowywanie odniesienia do wątku zadania i wywołanie interrupt() w tym wątku. W kodzie demo:

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; 
    } 
} 

a głównym sposobem jest:

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(); 
    } 
} 

Czy jest to dobre rozwiązanie, czy jest jakiś lepszy sposób, aby zatrzymać uruchomiony wątek w puli wątków?

+1

Wyciągnij wtyczkę;) – sje397

Odpowiedz

2

W swojej nadpisanej metodzie run() zapętlasz na zawsze z while(true). Standardowym zachowaniem byłby boolean runIndicator, który metoda run() ustawia na true po uruchomieniu, a twoja pętla powinna wtedy być while(runIndicator). Twoja metoda stop() powinna być prosta i powinna być ustawiona na runIndicator = false, aby wypłynęła kolejna iteracja pętli.

+0

W tej implementacji metoda 'stop()' powinna również wywoływać 'this.interrupt()'. W rzeczywistej implementacji powinna istnieć podobna metoda, której można użyć do zatrzymania tego, co dzieje się pod obciążeniem, zamiast wywołania 'Thread.sleep * (4000)'. Możesz użyć bardzo podobnej metody do 'runIndicator' aby to osiągnąć. –

+0

@Erick, 'interrupt()' nie jest metodą działania, więc nie jest to możliwe. 'Thread.currentThread(). Interrupt()' nie zadziałałoby albo dlatego, że wywołanie metody stop() dzieje się w innym wątku niż ten, który chcesz przerwać. – Thirler

+1

Przepraszam, nie złapałem tego. Zawsze zastępuję 'Thread' zamiast' Runnable', więc zawsze mam dostęp do tych rzeczy. Zrobiłbym to samo tutaj. Rozwiązałoby to również problem drobne problemy z wątkami zamieszczone w odpowiedzi. –

3

Idea twojego podejścia jest jednym z kilku poprawnych rozwiązań. Dealing with InterruptedException daje świetny przegląd sposobów korzystania z mechanizmu przerwań. Ten mechanizm jest szczególnie przydatny w przypadku długich obliczeń. Jeszcze jedną rzeczą, o której należy pamiętać, jest to, że inne biblioteki mogą zepsuć twój mechanizm przerwania, nie robiąc tego, co mówi przewodnik (nie resetując stanu przerwania, gdy go nie obsługują itp.).

Należy pamiętać, że twoja klasa Task nie jest bezpieczna dla wątków. Możesz zatrzymać zadanie przed zapisaniem currentThread, co dałoby wyjątek NullPointerException.

znacznie prostsze podejście jest ustawienie volatile boolean zmienna running i zamiast while(true) pętli robi podejście while(running) (jest to jednak znacznie bardziej ogólnie).

Kolejną rzeczą, na którą należy zwrócić uwagę, jest mechanizm FutureTask, ponieważ ma on już mechanizm anulowania, który wykorzystuje mechanizm przerwań.

1

ExecutorService.shutdown() i executorService.shutdownNow() powinny być używane do zamykania puli wątków w celu szybkiego zamknięcia aplikacji. Zobacz ExecutorService.

Zobacz odpowiedź Qwerky'ego na zakończenie bieżącego wątku.

+4

Chce zamknąć pojedynczy wątek, a nie całą pulę. –

+0

Ta odpowiedź jest podana przez Qwerky. Ale zamknięcie puli wątków jest również wymagane, aby z wdziękiem wyjść z aplikacji. –

2

można zatrzymać go trzymając odniesienie do tej przyszłości

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

logicznej jest - może przerwać jeśli uruchomiony.

Przetestowałem i otrzymałem przerwany wyjątek z tego wątku.

Jeśli cachedThreadPool masz może chcesz dokładnie sprawdzić, czy przechwycisz wyjątek w swoim runnable, a następnie nie przywracaj flagi przerwane, ponieważ wątek będzie działać w przyszłości, jeśli ustawisz przerwanie, druga kolejka przyszłość może nie działać.

Powiązane problemy