Potrzebuję rozwiązania, aby poprawnie zatrzymać wątek w Javie.
Mam IndexProcessor
klasę, która implementuje interfejs Runnable:
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
@Override
public void run() {
boolean run = true;
while (run) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
run = false;
}
}
}
}
I mam ServletContextListener
klasę, która rozpoczyna i zatrzymuje wątek:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
@Override
public void contextInitialized(ServletContextEvent event) {
thread = new Thread(new IndexProcessor());
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (thread != null) {
thread.interrupt();
LOGGER.debug("Thread successfully stopped.");
}
}
}
Ale kiedy zamykam tomcat, otrzymuję wyjątek w mojej klasie IndexProcessor:
2012-06-09 17:04:50,671 [Thread-3] ERROR IndexProcessor Exception
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at lt.ccl.searchengine.processor.IndexProcessor.run(IndexProcessor.java:22)
at java.lang.Thread.run(Unknown Source)
Używam JDK 1.6. Pytanie brzmi:
Jak mogę zatrzymać wątek i nie zgłaszać żadnych wyjątków?
PS Nie chcę używać .stop();
metody, ponieważ jest przestarzała.
java
multithreading
listener
Paulius Matulionis
źródło
źródło
InterruptedException
. Tak myślę, ale zastanawiam się też, jak to jest w standardowej formie.InterruptedException
można znaleźć na stronie ibm.com/developerworks/library/j-jtp05236 .Odpowiedzi:
W
IndexProcessor
klasie potrzebujesz sposobu ustawienia flagi, która informuje wątek, że będzie musiał zakończyć działanie, podobnie jak zmiennarun
, której użyłeś tylko w zakresie klasy.Kiedy chcesz zatrzymać wątek, ustaw tę flagę, wzywaj
join()
wątek i poczekaj, aż zakończy się.Upewnij się, że flaga jest bezpieczna dla wątków, używając zmiennej lotnej lub metod pobierających i ustawiających, które są zsynchronizowane ze zmienną używaną jako flaga.
Następnie w
SearchEngineContextListener
:źródło
Używanie
Thread.interrupt()
jest całkowicie akceptowalnym sposobem na zrobienie tego. W rzeczywistości jest to prawdopodobnie lepsze niż flaga, jak sugerowano powyżej.Thread.sleep
Wynika to z faktu, że jeśli prowadzisz przerywaną rozmowę blokującą (np. Lub korzystasz z operacji na kanale java.nio), będziesz w stanie od razu oderwać się od nich.Jeśli używasz flagi, musisz poczekać na zakończenie operacji blokowania, a następnie możesz sprawdzić swoją flagę. W niektórych przypadkach i tak musisz to zrobić, na przykład używając standardowego
InputStream
/OutputStream
które nie są przerywane.W takim przypadku, gdy wątek zostanie przerwany, nie spowoduje to przerwania operacji we / wy, jednak można to łatwo zrobić rutynowo w kodzie (i należy to zrobić w strategicznych punktach, w których można bezpiecznie zatrzymać i wyczyścić)
Jak powiedziałem, główną zaletą
Thread.interrupt()
jest to, że możesz natychmiast zerwać z przerwanych połączeń, czego nie można zrobić przy podejściu flagowym.źródło
interrupt()
może być w porządku, ale w wielu innych przypadkach tak nie jest (na przykład, jeśli zasób musi zostać zamknięty). Jeśli ktoś zmieni wewnętrzne działanie pętli, musisz pamiętać o przejściuinterrupt()
na tryb boolowski. Od samego początku wybrałem bezpieczną drogę i użyłem flagi.Prosta odpowiedź: możesz zatrzymać wątek WEWNĘTRZNIE na jeden z dwóch typowych sposobów:
Możesz także zatrzymać wątki ZEWNĘTRZNIE:
system.exit
(to zabija cały proces)interrupt()
metodę obiektu wątku *kill()
lubstop()
)*: Oczekuje się, że ma to zatrzymać wątek. Jednak to, co faktycznie robi wątek, gdy tak się dzieje, zależy wyłącznie od tego, co napisał programista, gdy utworzyli implementację wątku.
Typowy wzorzec, który widzisz w implementacjach metod uruchamiania, to taki
while(boolean){}
, gdzie boolean ma zwykle coś o nazwieisRunning
, jest zmienną składową swojej klasy wątku, jest zmienny i zazwyczaj dostępny dla innych wątków za pomocą metod ustawiających, npkill() { isRunnable=false; }
. Te podprogramy są fajne, ponieważ pozwalają wątkowi zwolnić wszystkie posiadane zasoby przed zakończeniem.źródło
Zawsze powinieneś kończyć wątki, sprawdzając flagę w
run()
pętli (jeśli istnieje).Twój wątek powinien wyglądać następująco:
Następnie możesz zakończyć wątek, dzwoniąc
thread.stopExecuting()
. W ten sposób nić zostaje zakończona czysto, ale zajmuje to do 15 sekund (z powodu twojego snu). Nadal możesz wywołać thread.interrupt (), jeśli jest to naprawdę pilne - ale preferowanym sposobem powinno być zawsze sprawdzanie flagi.Aby uniknąć czekania przez 15 sekund, możesz podzielić sen w następujący sposób:
źródło
Thread
- implementujeRunnable
- nie można wywoływaćThread
metod, chyba że zadeklarujesz to jako,Thread
w którym to przypadku nie możesz zadzwonićstopExecuting()
Zazwyczaj wątek jest przerywany, gdy zostanie przerwany. Dlaczego więc nie użyć natywnej wartości logicznej? Spróbuj isInterrupted ():
ref- Jak mogę zabić wątek? bez użycia stop ();
źródło
Do synchronizacji wątków wolę używać,
CountDownLatch
który pomaga wątkom czekać na zakończenie procesu. W takim przypadku klasa procesu roboczego jest konfigurowana zCountDownLatch
instancją o podanej liczbie. Wywołanieawait
metody będzie blokowane, dopóki bieżąca liczba nie osiągnie zera z powodu wywołaniacountDown
metody lub osiągnięcia limitu czasu. Takie podejście umożliwia natychmiastowe przerwanie wątku bez konieczności oczekiwania na upływ określonego czasu oczekiwania:Gdy chcesz zakończyć wykonanie innego wątku, wykonać na odliczanie
CountDownLatch
ijoin
gwint do głównego wątku:źródło
Niektóre dodatkowe informacje. Zarówno flaga, jak i przerwanie są sugerowane w dokumencie Java.
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
W przypadku wątku, który czeka przez długi czas (np. Na dane wejściowe), użyj
Thread.interrupt
źródło
Nie udało mi się przerwać pracy w Androidzie, więc użyłem tej metody, działa idealnie:
źródło
volatile
. Zobacz docs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3 .Kiedyś spróbuję 1000 razy w mojej funkcji onDestroy () / contextDestroyed ()
źródło