Jak zabić java.lang.Thread
w Javie?
ExecutorStatus
tego pytania: stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Jak zabić java.lang.Thread
w Javie?
ExecutorStatus
tego pytania: stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Odpowiedzi:
Zobacz ten wątek firmy Sun, dlaczego są one przestarzałeThread.stop()
. Szczegółowo wyjaśnia, dlaczego była to zła metoda i co należy zrobić, aby bezpiecznie zatrzymać wątki w ogóle.
Zalecają, aby używać zmiennej wspólnej jako flagi, która prosi wątek w tle o zatrzymanie. Ta zmienna może być następnie ustawiona przez inny obiekt żądający zakończenia wątku.
getConnection()
z java.sql.DriverManager
. Jeśli próba połączenia trwa zbyt długo, próbuję zabić odpowiedni wątek, dzwoniąc, Thread.interrupt()
ale nie ma to żadnego wpływu na wątek. Thread.stop()
Działa jednak, choć Oracle twierdzi, że nie powinno działać, jeśli interrupt()
nie robi. Zastanawiam się, jak to działa i unikaj stosowania przestarzałej metody.
Generalnie nie ..
Poprosisz go, aby przerwał cokolwiek robi za pomocą Thread.interrupt () (link javadoc)
Dobre wyjaśnienie, dlaczego znajduje się w javadoc tutaj (link do java technote)
interrupt()
wywoływana jest metoda? Główne pytanie dotyczy generowania dziennika dla każdego nowego wątku.
W Javie wątki nie są zabijane, ale zatrzymanie wątku odbywa się w trybie współpracy . Wątek jest proszony o zakończenie, a następnie można go bezpiecznie zamknąć.
Często volatile boolean
używane jest pole, które wątek okresowo sprawdza i kończy, gdy zostanie ustawione na odpowiednią wartość.
Nie użyłbym a, boolean
aby sprawdzić, czy wątek powinien się zakończyć . Jeśli użyjesz go volatile
jako modyfikatora pola, będzie to działało niezawodnie, ale jeśli kod stanie się bardziej złożony, ponieważ zamiast tego używa innych metod blokowania w while
pętli, może się zdarzyć, że Twój kod w ogóle się nie zakończy lub przynajmniej zajmie więcej czasu, ponieważ może chcieć.
Niektóre metody bibliotek blokujących obsługują przerwanie.
Każdy wątek ma już status przerwania flagi logicznej i powinieneś z niego skorzystać. Można to zaimplementować w następujący sposób:
public void run() {
try {
while (!interrupted()) {
// ...
}
} catch (InterruptedException consumed)
/* Allow thread to exit */
}
}
public void cancel() { interrupt(); }
Kod źródłowy zaadaptowany z Java Concurrency in Practice . Ponieważ cancel()
metoda jest publiczna, możesz pozwolić innemu wątkowi wywoływać tę metodę, jak chcesz.
Jednym ze sposobów jest ustawienie zmiennej klasy i użycie jej jako wartownika.
Class Outer {
public static volatile flag = true;
Outer() {
new Test().start();
}
class Test extends Thread {
public void run() {
while (Outer.flag) {
//do stuff here
}
}
}
}
Ustaw zmienną klasy zewnętrznej, tj. Flag = true w powyższym przykładzie. Ustaw wartość false, aby „zabić” wątek.
volatile
aby upewnić się, że działa poprawnie wszędzie. Klasa wewnętrzna nie jest statyczna, więc flaga powinna być zmienną instancji. Flaga powinna zostać wyczyszczona metodą akcesora, aby można było wykonać inne operacje (takie jak przerwanie). Nazwa „flaga” nie ma charakteru opisowego.
Jest sposób, jak to zrobić. Ale jeśli musiałeś go użyć, albo jesteś złym programistą, albo używasz kodu napisanego przez złych programistów. Powinieneś więc pomyśleć o tym, aby przestać być złym programistą lub przestać używać tego złego kodu. To rozwiązanie dotyczy tylko sytuacji, gdy NIE MA INNEGO SPOSOBU.
Thread f = <A thread to be stopped>
Method m = Thread.class.getDeclaredMethod( "stop0" , new Class[]{Object.class} );
m.setAccessible( true );
m.invoke( f , new ThreadDeath() );
Thread.stop
nawet jeśli jest przestarzała.
Thread.stop
robi to samo, ale sprawdza także dostęp i uprawnienia. Używanie Thread.stop
jest raczej oczywiste i nie pamiętam powodu, dla którego go użyłem Thread.stop0
. Może Thread.stop
nie działało w moim specjalnym przypadku (Weblogic na Javie 6). A może dlatego, że Thread.stop
jest przestarzały i powoduje ostrzeżenie.
Chcę dodać kilka obserwacji na podstawie zebranych komentarzy.
Thread.stop()
zatrzyma wątek, jeśli menedżer bezpieczeństwa na to zezwoli.Thread.stop()
jest niebezpieczny. Powiedziawszy to, jeśli pracujesz w środowisku JEE i nie masz kontroli nad wywoływanym kodem, może być konieczne; zobacz Dlaczego Thread.stop jest przestarzałe?stop()
tworzy nowy ThreadDeathError
błąd w wątku wywołującym, a następnie zgłasza ten błąd w wątku docelowym . Dlatego ślad stosu jest generalnie bezwartościowy.stop()
sprawdza u menedżera bezpieczeństwa, a następnie wywołuje stop1()
te połączenia stop0()
. stop0()
jest rodzimym kodem.Thread.stop()
nie została jeszcze usunięta), ale Thread.stop(Throwable)
została usunięta w wersji Java 11. ( lista mailingowa , JDK-8204243 )Głosowałbym na Thread.stop()
.
Na przykład masz długotrwałą operację (jak żądanie sieciowe). Podobno czekasz na odpowiedź, ale może to zająć trochę czasu, a użytkownik przejdzie do innego interfejsu użytkownika. Ten oczekujący wątek jest teraz a) bezużyteczny b) potencjalny problem, ponieważ kiedy uzyska wynik, jest całkowicie bezużyteczny i wywoła wywołania zwrotne, które mogą prowadzić do wielu błędów.
Wszystko to, a on może przetwarzać odpowiedzi, które mogą wymagać dużej mocy obliczeniowej. A Ty, jako programista, nie możesz nawet tego zatrzymać, ponieważ nie możesz rzucać if (Thread.currentThread().isInterrupted())
wierszy w całym kodzie.
Więc niemożność silnego zatrzymania nici jest dziwna.
Thread.stop()
bezpiecznie dzwonić . Nie głosujesz za Thread.stop()
, prosisz o każdą osobę, która realizuje każdą operację, która może zająć dużo czasu, aby bezpiecznie ją przerwać. I może to być dobry pomysł, ale nie ma to nic wspólnego z wdrażaniem Thread.stop()
jako sposobu żądania bezpiecznej aborcji. Już to mamy interrupt
.
stop
.
Pytanie jest raczej niejasne. Jeśli miałeś na myśli „jak napisać program, aby wątek przestał działać, kiedy tego chcę”, wówczas różne inne odpowiedzi powinny być pomocne. Ale jeśli miałeś na myśli „Mam awarię z serwerem, nie mogę teraz zrestartować się i potrzebuję tylko konkretnego wątku, aby umrzeć, chodź co może”, to potrzebujesz narzędzia interwencyjnego, aby dopasować takie narzędzia monitorowania jstack
.
W tym celu stworzyłem jkillthread . Zobacz instrukcję użytkowania.
Oczywiście zdarza się, że uruchamiasz jakiś niezupełnie zaufany kod. (Mam to osobiście, umożliwiając wykonywanie przesłanych skryptów w moim środowisku Java. Tak, wszędzie dzwoni alarm bezpieczeństwa, ale jest to część aplikacji.) W tym niefortunnym przypadku po prostu masz nadzieję, pytając pisarzy skryptów szanować jakiś rodzaj logicznego sygnału uruchomienia / nie uruchomienia. Jedynym przyzwoitym zabezpieczeniem przed awarią jest wywołanie metody stop w wątku, jeśli, powiedzmy, działa on dłużej niż pewien limit czasu.
Ale jest to po prostu „przyzwoite”, a nie bezwzględne, ponieważ kod może wychwycić błąd ThreadDeath (lub jakikolwiek wyjątek, który jawnie zgłaszasz) i nie wyrzucać go tak, jak powinien to zrobić dżentelmen. Tak więc, podstawową kwestią jest AFAIA, nie ma absolutnie bezpiecznej awarii.
Nie ma sposobu, aby z wdziękiem zabić wątek.
Możesz spróbować przerwać wątek, jedną wspólną strategią jest użycie trującej pigułki, aby wysłać wiadomość do wątku, aby się zatrzymał
public class CancelSupport {
public static class CommandExecutor implements Runnable {
private BlockingQueue<String> queue;
public static final String POISON_PILL = “stopnow”;
public CommandExecutor(BlockingQueue<String> queue) {
this.queue=queue;
}
@Override
public void run() {
boolean stop=false;
while(!stop) {
try {
String command=queue.take();
if(POISON_PILL.equals(command)) {
stop=true;
} else {
// do command
System.out.println(command);
}
} catch (InterruptedException e) {
stop=true;
}
}
System.out.println(“Stopping execution”);
}
}
}
BlockingQueue<String> queue=new LinkedBlockingQueue<String>();
Thread t=new Thread(new CommandExecutor(queue));
queue.put(“hello”);
queue.put(“world”);
t.start();
Thread.sleep(1000);
queue.put(“stopnow”);
Zasadniczo nie zabijasz, nie zatrzymujesz ani nie przerywasz wątku (ani nie sprawdzasz, czy jest przerwany ()), ale pozwalasz mu zakończyć się naturalnie.
To jest proste. Możesz użyć dowolnej pętli razem z (lotną) zmienną logiczną wewnątrz metody run () do kontrolowania aktywności wątku. Możesz także powrócić z aktywnego wątku do głównego wątku, aby go zatrzymać.
W ten sposób z wdziękiem zabijasz wątek :).
Próby nagłego zakończenia wątku są dobrze znaną złą praktyką programistyczną i dowodem złego projektowania aplikacji. Wszystkie wątki w aplikacji wielowątkowej jawnie i niejawnie współużytkują ten sam stan procesu i są zmuszone do współpracy ze sobą, aby zachować spójność, w przeciwnym razie twoja aplikacja będzie podatna na błędy, które będą naprawdę trudne do zdiagnozowania. Dlatego obowiązkiem programisty jest zapewnienie takiej spójności poprzez staranne i przejrzyste projektowanie aplikacji.
Istnieją dwa główne właściwe rozwiązania kontrolowanych zakończeń wątków:
Dobre i szczegółowe wyjaśnienie problemów związanych z nagłym zakończeniem wątków, a także przykłady niewłaściwych i właściwych rozwiązań kontrolowanego zakończenia wątków można znaleźć tutaj:
Nie udało mi się przerwać pracy w Androidzie, więc użyłem tej metody, działa idealnie:
boolean shouldCheckUpdates = true;
private void startupCheckForUpdatesEveryFewSeconds() {
Thread t = new Thread(new CheckUpdates());
t.start();
}
private class CheckUpdates implements Runnable{
public void run() {
while (shouldCheckUpdates){
//Thread sleep 3 seconds
System.out.println("Do your thing here");
}
}
}
public void stop(){
shouldCheckUpdates = false;
}
„Zabicie wątku” nie jest właściwym zwrotem. Oto jeden ze sposobów, w jaki możemy zaimplementować płynne zakończenie / wyjście z wątku:
Runnable, którego użyłem:
class TaskThread implements Runnable {
boolean shouldStop;
public TaskThread(boolean shouldStop) {
this.shouldStop = shouldStop;
}
@Override
public void run() {
System.out.println("Thread has started");
while (!shouldStop) {
// do something
}
System.out.println("Thread has ended");
}
public void stop() {
shouldStop = true;
}
}
Klasa wyzwalająca:
public class ThreadStop {
public static void main(String[] args) {
System.out.println("Start");
// Start the thread
TaskThread task = new TaskThread(false);
Thread t = new Thread(task);
t.start();
// Stop the thread
task.stop();
System.out.println("End");
}
}
Thread.stop jest przestarzałe, więc jak zatrzymać wątek w Javie?
Zawsze używaj metody przerwania i w przyszłości, aby poprosić o anulowanie
Callable < String > callable = new Callable < String > () {
@Override
public String call() throws Exception {
String result = "";
try {
//assume below take method is blocked as no work is produced.
result = queue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return result;
}
};
Future future = executor.submit(callable);
try {
String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
logger.error("Thread timedout!");
return "";
} finally {
//this will call interrupt on queue which will abort the operation.
//if it completes before time out, it has no side effects
future.cancel(true);
}
public interface CustomCallable < T > extends Callable < T > {
void cancel();
RunnableFuture < T > newTask();
}
public class CustomExecutorPool extends ThreadPoolExecutor {
protected < T > RunnableFuture < T > newTaskFor(Callable < T > callable) {
if (callable instanceof CancellableTask)
return ((CancellableTask < T > ) callable).newTask();
else
return super.newTaskFor(callable);
}
}
public abstract class UnblockingIOTask < T > implements CustomCallable < T > {
public synchronized void cancel() {
try {
obj.close();
} catch (IOException e) {
logger.error("io exception", e);
}
}
public RunnableFuture < T > newTask() {
return new FutureTask < T > (this) {
public boolean cancel(boolean mayInterruptIfRunning) {
try {
this.cancel();
} finally {
return super.cancel(mayInterruptIfRunning);
}
}
};
}
}