2012-05-08 15 views
33

Ilekroć dzwonię pod numer shutdownNow() lub shutdown(), nie wyłącza się. Czytałem o kilku wątkach, w których mówiło się, że wyłączanie nie jest gwarantowane - czy ktoś może mi zapewnić dobry sposób robienia tego?Jak zamknąć ExecutorService?

+0

Proszę zaznaczyć właściwą odpowiedź jako zaakceptowany – gonephishing

Odpowiedz

74

Typowy wzór jest:

executorService.shutdownNow(); 
executorService.awaitTermination(); 

Dzwoniąc shutdownNow, wykonawca będzie (ogólnie) starają się przerwać nici, którymi zarządza. Aby zamknięcie było pełne wdzięku, musisz przechwycić przerwany wyjątek w wątkach lub sprawdzić stan przerwania. Jeśli nie, twoje wątki będą działać wiecznie, a twój executor nigdy nie będzie w stanie się zamknąć. Dzieje się tak dlatego, że przerwanie wątków w Javie jest procesem opartym na współpracy (tzn. Kod przerywany musi coś zrobić, gdy zostanie poproszony o zatrzymanie, a nie kod przerywający kod).

Na przykład poniższy kod drukuje Exiting normally.... Ale jeśli skomentujesz linię if (Thread.currentThread().isInterrupted()) break;, wydrukuje ona Still waiting..., ponieważ wątki w executorze nadal działają.

public static void main(String args[]) throws InterruptedException { 
    ExecutorService executor = Executors.newFixedThreadPool(1); 
    executor.submit(new Runnable() { 

     @Override 
     public void run() { 
      while (true) { 
       if (Thread.currentThread().isInterrupted()) break; 
      } 
     } 
    }); 

    executor.shutdownNow(); 
    if (!executor.awaitTermination(100, TimeUnit.MICROSECONDS)) { 
     System.out.println("Still waiting..."); 
     System.exit(0); 
    } 
    System.out.println("Exiting normally..."); 
} 

Alternatywnie, może to być napisane z InterruptedException tak:

public static void main(String args[]) throws InterruptedException { 
    ExecutorService executor = Executors.newFixedThreadPool(1); 
    executor.submit(new Runnable() { 

     @Override 
     public void run() { 
      try { 
       while (true) {Thread.sleep(10);} 
      } catch (InterruptedException e) { 
       //ok let's get out of here 
      } 
     } 
    }); 

    executor.shutdownNow(); 
    if (!executor.awaitTermination(100, TimeUnit.MICROSECONDS)) { 
     System.out.println("Still waiting..."); 
     System.exit(0); 
    } 
    System.out.println("Exiting normally..."); 
} 
+0

wiesz, jeśli zamknięcie inicjuje również 'Thread.interrupt()', z Java API nie robi wydaje się, że 'shutdown' przerywa wątki? – Bionix1441

+1

@ Bionix1441 Nie, nie ma. 'shutdownNow' działa jednak. – assylias

15

Najlepszym sposobem jest to, co faktycznie mają w javadoc, która jest:

Poniższe zamykają metoda w dół ExecutorService w dwóch fazach, najpierw przez wywołanie shutdown, aby odrzucić przychodzące zadania, a następnie wywołanie shutdownNow, jeśli to konieczne, aby anulować jakiekolwiek długotrwałe zadania:

void shutdownAndAwaitTermination(ExecutorService pool) { 
    pool.shutdown(); // Disable new tasks from being submitted 
    try { 
     // Wait a while for existing tasks to terminate 
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { 
      pool.shutdownNow(); // Cancel currently executing tasks 
      // Wait a while for tasks to respond to being cancelled 
      if (!pool.awaitTermination(60, TimeUnit.SECONDS)) 
       System.err.println("Pool did not terminate"); 
     } 
    } catch (InterruptedException ie) { 
     // (Re-)Cancel if current thread also interrupted 
     pool.shutdownNow(); 
     // Preserve interrupt status 
     Thread.currentThread().interrupt(); 
    } 
}