Kto wywołuje metodę przerwania wątku Java (), jeśli ja nie?

87

Przeczytałem i ponownie przeczytałem Java Concurrency in Practice, przeczytałem kilka wątków na ten temat, przeczytałem artykuł IBM Dealing with InterruptedException, a jednak jest coś, czego po prostu nie rozumiem, a myślę, że można to zepsuć na dwa pytania:

  1. Jeśli sam nigdy nie przerywam innych wątków, co może wywołać wyjątek InterruptedException ?

  2. Jeśli sam nigdy nie przerywam innych wątków, używając przerwania () (powiedzmy, ponieważ używam innych środków do anulowania moich wątków roboczych, takich jak trujące pigułki i podczas (! Anulowana) pętla stylu [jak wyjaśniono w JCIP]), co oznacza wtedy InterruptedException ? Co mam zrobić po złapaniu jednego? Czy zamknąć moją aplikację?

Składnia T3rr0r
źródło

Odpowiedzi:

50

Mechanizm przerwania wątku jest preferowanym sposobem uzyskania (współpracującego) wątku, aby odpowiedział na żądanie zatrzymania tego, co robi. Każdy wątek (w tym myślę, że sam wątek) mógłby wywołać interrupt()wątek.

W praktyce normalne przypadki użycia interrupt()obejmują pewien rodzaj frameworka lub menedżera, który nakazuje wątkowi robocemu przerwanie tego, co robią. Jeśli wątek roboczy jest „świadomy przerwania”, zauważy, że został przerwany przez wyjątek lub przez okresowe sprawdzanie flagi przerwania. Zauważając, że został przerwany, grzeczny wątek porzuciłby to, co robi i zakończył się sam.

Zakładając powyższy przypadek użycia, twój kod prawdopodobnie zostanie przerwany, jeśli zostanie uruchomiony w środowisku Java lub z jakiegoś wątku roboczego. A kiedy zostanie przerwany, twój kod powinien porzucić to, co robi i doprowadzić do końca w najbardziej odpowiedni sposób. W zależności od tego, jak został wywołany Twój kod, można to zrobić, zwracając lub zgłaszając odpowiedni wyjątek. Ale chyba nie powinien dzwonić System.exit(). (Twoja aplikacja niekoniecznie wie, dlaczego została przerwana, a na pewno nie wie, czy istnieją inne wątki, które muszą zostać przerwane przez framework).

Z drugiej strony, jeśli twój kod nie jest przeznaczony do uruchamiania pod kontrolą jakiegoś frameworka, możesz argumentować, że InterruptedExceptionjest to nieoczekiwany wyjątek; tj. błąd. W takim przypadku wyjątek należy traktować tak, jak inne błędy; np. zawiń go w niesprawdzony wyjątek i złap go i zarejestruj w tym samym miejscu, w którym masz do czynienia z innymi nieoczekiwanymi, niezaznaczonymi wyjątkami. (Alternatywnie, Twoja aplikacja może po prostu zignorować przerwanie i kontynuować to, co robiła).


1) Jeśli sam nigdy nie przerywam innych wątków, co może wywołać wyjątek InterruptedException?

Jednym z przykładów jest sytuacja, w której Runnableobiekty są wykonywane przy użyciu ExecutorServicei shutdownNow()jest wywoływana w usłudze. Teoretycznie każda pula wątków lub platforma zarządzania wątkami innej firmy może legalnie zrobić coś takiego.

2) Jeśli sam nigdy nie przerywam innych wątków, używając przerwania () ... co oznacza InterruptedExceptionthen? Co mam zrobić po złapaniu jednego? Czy zamknąć moją aplikację?

Musisz przeanalizować kod źródłowy, aby dowiedzieć się, co interrupt()wywołuje połączenia i dlaczego. Gdy już to ustalisz, możesz dowiedzieć się, co musi zrobić >> Twoja << część aplikacji.

Dopóki nie wiesz, dlaczego InterruptedExceptionjest rzucany, radziłbym potraktować to jako poważny błąd; np. wydrukuj stos do pliku dziennika i zamknij aplikację. (Oczywiście nie zawsze jest to właściwa odpowiedź ... ale chodzi o to, że jest to „błąd”, na który należy zwrócić uwagę programisty / opiekuna).

3) Jak mogę się dowiedzieć, kto / co dzwoni interrupt()?

Nie ma na to dobrej odpowiedzi. Najlepsze, co mogę zasugerować, to ustawić punkt przerwania Thread.interrupt()i spojrzeć na stos wywołań.

Stephen C.
źródło
12

Jeśli zdecydujesz się zintegrować swój kod z innymi bibliotekami, mogą one odwołać interrupt()się do Twojego kodu. np. jeśli zdecydujesz się w przyszłości wykonać kod w ramach usługi ExecutorService , może to wymusić zamknięcie systemu za pośrednictwem interrupt().

Krótko mówiąc, zastanawiałbym się nie tylko, gdzie twój kod jest teraz uruchomiony , ale w jakim kontekście może działać w przyszłości. np. czy zamierzasz umieścić to w bibliotece? Pojemnik ? Jak będą go używać inni ludzie? Czy zamierzasz go ponownie użyć?

Brian Agnew
źródło
Myślałem, że tylko shutdownNow wywołuje metodę przerwania (). Czy dotyczy to również zamykania?
Harinder
9

Jak zauważyli inni, przerwanie wątku (a właściwie przerwanie połączenia blokującego) jest zwykle używane do celów czystego wyjścia lub anulowania trwającej czynności.

Nie powinieneś jednak traktować InterruptedExceptionsamego siebie jako „polecenia wyjścia”. Zamiast tego powinieneś myśleć o przerwaniach jako o sposobie kontrolowania stanu działania wątków, w podobny sposób jak to Object.notify()robi. W ten sam sposób, w jaki sprawdzałbyś aktualny stan po obudzeniu się z połączenia do Object.wait()(nie zakładasz, że przebudzenie oznacza spełnienie warunku oczekiwania), po szturchnięciu Cię przerwaniem powinieneś sprawdzić, dlaczego zostałeś przerwany . Zwykle jest na to sposób. Na przykład java.util.concurrent.FutureTaskma isCancelled()metodę.

Przykład kodu:

public void run() {
    ....
    try {
        .... // Calls that may block.
    } catch (InterruptedException e) {
        if (!running) {  // Add preferred synchronization here.
            return; // Explicit flag says we should stop running.
        }
        // We were interrupted, but the flag says we're still running.
        // It would be wrong to always exit here. The interrupt 'nudge'
        // could mean something completely different. For example, it
        // could be that the thread was blocking on a read from a particular
        // file, and now we should read from a different file.
        // Interrupt != quit (not necessarily).
    }
    ....
}
public void stop() {
    running = false; // Add preferred synchronization here.
    myThread.interrupt();
}
Øyvind Bakksjø
źródło
3

Problem z pytaniem to „ja”. „I” zwykle odnosi się do pojedynczego wystąpienia klasy. Rozumiem przez to, że żaden konkretny fragment kodu (klasy) niskiego poziomu nie powinien polegać na implementacji całego systemu. Powiedziawszy, że musisz podjąć pewne decyzje „architektoniczne” (np. Na jakiej platformie uruchomić).

Możliwe nieoczekiwane przerwania pochodzące ze środowiska JRE to anulowane zadania w java.util.concurrentapletach i zamykanie ich.

Obsługa przerwań wątków jest zwykle napisana niepoprawnie. Dlatego proponuję decyzję architektoniczną, aby w miarę możliwości unikać powodowania przerw. Jednak przerwania obsługi kodu powinny być zawsze napisane poprawnie. Nie można teraz usuwać przerw z platformy.

Tom Hawtin - haczyk
źródło
Cześć Tom, pamiętam twoje imię z cljp;) Cóż, dokładnie: sam nigdy nie musiałem przerywać () ... Chyba że łapię InterruptedException i muszę ponownie potwierdzić stan przerwania, ale to nadal nie jest w 100% jasne dla mnie. Jestem tu nowy i jestem zaskoczony liczbą głosów za i odpowiedzi / komentarzy (zarówno tych poprawnych, jak i złych): oczywiście jest to temat nietrywialny, a przynajmniej zwykle słabo wyjaśniony. To powiedziawszy dzięki wszystkim
postom,
3

Możesz się tego nauczyć, tworząc własną klasę wątku (rozszerzającą java.lang.Thread) i interrupt()metodę nadpisywania , w której zapisujesz ślad stosu, powiedzmy, w polu String, a następnie przenosisz do super.interrupt ().

public class MyThread extends Thread {

    public volatile String interruptStacktrace; // Temporary field for debugging purpose.

    @Override
    public void interrupt() {
        interruptStacktrace = dumpStack(); // You implement it somehow...

        super.interrupt();
    }
}
Weekens
źródło
1

Jak już wspomniano, inna biblioteka może przerwać twoje wątki. Nawet jeśli biblioteka nie ma jawnego dostępu do wątków z twojego kodu, nadal mogą uzyskać listę uruchomionych wątków i przerwać je w ten sposób za pomocą następującej metody .

mangoPijany
źródło
1

Myślę, że rozumiem, dlaczego jesteś trochę zdezorientowany, jeśli chodzi o przerwanie. Proszę rozważyć moje odpowiedzi w kolejce:

Jeśli sam nigdy nie przerywam innych wątków, co może wywołać wyjątek InterruptedException ?

Po pierwsze możesz przerwać inne wątki; Wiem, że w JCiP jest napisane, że nigdy nie należy przerywać wątków, których nie jesteś właścicielem; jednak to stwierdzenie musi być właściwie zrozumiane. Oznacza to, że Twój kod, który może być uruchomiony w dowolnym wątku, nie powinien obsługiwać przerwania, ponieważ ponieważ nie jest właścicielem wątku, nie ma pojęcia o jego polityce przerywania. Możesz więc poprosić o przerwanie w innych wątkach, ale pozwól jego właścicielowi podjąć działanie przerywające; zawiera w sobie politykę przerwania, a nie kod zadania; przynajmniej bądź uprzejmy i ustaw flagę przerwania!

Istnieje wiele powodów, dla których mogą nadal występować przerwy, mogą to być przekroczenia limitu czasu, przerwania JVM itp.

Jeśli sam nigdy nie przerywam innych wątków, używając przerwania () (powiedzmy, ponieważ używam innych środków do anulowania moich wątków roboczych, takich jak trujące tabletki i podczas (! Anulowano) pętlę stylu [jak wyjaśniono w JCIP]), co oznacza wtedy InterruptedException? Co mam zrobić po złapaniu jednego? Czy zamknąć moją aplikację?

Musisz tutaj bardzo uważać; Jeśli jesteś właścicielem wątku, który wywołał InterruptedException (IE), to wiesz, co zrobić po jego przechwyceniu, powiedz, że możesz zamknąć aplikację / usługę lub zastąpić ten zabity wątek nowym! Jeśli jednak nie jesteś właścicielem wątku, po złapaniu IE albo wyrzuć go ponownie wyżej na stos wywołań, albo po zrobieniu czegoś (może być rejestrowanie), zresetuj stan przerwania, aby kod, który jest właścicielem tego wątku, gdy kontrola do niego dotrze, może dowiaduje się, że wątek został przerwany, a zatem podejmuje działania tak, jak chce, ponieważ tylko on zna zasady przerywania.

Mam nadzieję, że to pomogło.

nawazish-stackoverflow
źródło
0

InterruptedExceptionMówi, że procedura może zostać przerwany, ale nie koniecznie, że to będzie.

Jeśli nie spodziewasz się przerwania, powinieneś traktować je tak, jak każdy inny nieoczekiwany wyjątek. Jeśli znajduje się w krytycznej sekcji, w której nieoczekiwany wyjątek może mieć haniebne konsekwencje, najlepiej będzie spróbować wyczyścić zasoby i bezpiecznie zamknąć (ponieważ otrzymanie przerwania sygnalizuje, że używana jest dobrze zaprojektowana aplikacja, która nie polega na przerwaniach w pewnym sensie nie zostało zaprojektowane, więc coś musi być nie tak). Alternatywnie, jeśli dany kod jest czymś niekrytycznym lub trywialnym, możesz zignorować (lub zarejestrować) przerwanie i kontynuować.

Popiół
źródło