Heim  >  Artikel  >  Java  >  So beenden Sie einen Thread in Java

So beenden Sie einen Thread in Java

WBOY
WBOYnach vorne
2023-04-26 08:58:201121Durchsuche

Das Geheimnis der Deaktivierung von Thread.stop

Auf die Frage, wie man einen Thread beendet, wissen wahrscheinlich die meisten Leute, dass man die Thread.stop-Methode aufrufen kann.

Aber diese Methode wird nach jdk1.2 nicht mehr empfohlen.

Sehen wir uns zunächst die Definition dieser Methode an:

  @Deprecated(since="1.2")
    public final void stop() {
        @SuppressWarnings("removal")
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            checkAccess();
            if (this != Thread.currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }
        // A zero status value corresponds to "NEW", it can't change to
        // not-NEW because we hold the lock.
        if (threadStatus != 0) {
            resume(); // Wake up thread if it was suspended; no-op otherwise
        }

        // The VM can handle all thread states
        stop0(new ThreadDeath());
    }

Wir können dem Code entnehmen, dass die Stop-Methode zunächst erkennt, ob eine Thread-Zugriffsberechtigung vorliegt. Wenn Sie über die Berechtigung verfügen, stellen Sie fest, ob der aktuelle Thread ein neu erstellter Thread ist. Wenn nicht, rufen Sie die Resume-Methode auf, um den angehaltenen Status des Threads aufzuheben.

Rufen Sie abschließend die Methode stop0 auf, um den Thread zu beenden.

Resume und stop0 sind zwei native Methoden, und die spezifische Implementierung wird hier nicht besprochen.

Es scheint, dass die Stoppmethode sinnvoll ist und kein Problem darstellt. Warum ist diese Methode unsicher?

Als nächstes schauen wir uns ein Beispiel an.

Wir erstellen eine NumberCounter-Klasse. Diese Klasse verfügt über eine sichere Methode zum Erhöhen der Zahl, mit der eins zur Zahl hinzugefügt wird:

public class NumberCounter {
    //要保存的数字
    private volatile int number=0;
    //数字计数器的逻辑是否完整
    private volatile boolean flag = false;

    public synchronized int increaseNumber() throws InterruptedException {
        if(flag){
            //逻辑不完整
            throw new RuntimeException("逻辑不完整,数字计数器未执行完毕");
        }
        //开始执行逻辑
        flag = true;
        //do something
        Thread.sleep(5000);
        number++;
        //执行完毕
        flag=false;
        return number;
    }
}

Tatsächlich wird in der tatsächlichen Arbeit z Eine Methode Die Ausführung kann lange dauern. Daher simulieren wir diesen zeitaufwändigen Vorgang hier, indem wir Thread.sleep aufrufen.

Hier haben wir auch einen Flag-Parameter, der markiert, ob die raiseNumber-Methode erfolgreich ausgeführt wurde.

Okay, als nächstes rufen wir die Methode dieser Klasse in einem Thread auf und sehen, was passiert:

    public static void main(String[] args) throws InterruptedException {
        NumberCounter numberCounter= new NumberCounter();
        Thread thread = new Thread(()->{
            while (true){
                try {
                    numberCounter.increaseNumber();
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(3000);
        thread.stop();
        numberCounter.increaseNumber();
    }

Hier erstellen wir einen Thread und warten auf diesen Thread, nachdem wir ihn für 3 ausgeführt haben Sekunden wird die Methode thread.stop direkt aufgerufen und wir finden die folgende Ausnahme:

Exception in thread „main“ java.lang.RuntimeException: Die Logik ist unvollständig, der Zahlenzähler wurde nicht ausgeführt
bei com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
bei com.flydean.Main.main(Main.java:18)

#🎜🎜 #
Dies liegt daran, dass die thread.stop-Methode die Ausführung des Threads direkt beendet, was dazu führt, dass mberCounter.increaseNumber nicht abgeschlossen wird.

Aber dieser unvollendete Zustand ist verborgen. Wenn Sie die Methode thread.stop verwenden, um den Thread zu beenden, führt dies wahrscheinlich zu unbekannten Ergebnissen.

Wir sagen also, thread.stop ist unsicher.

So gehen Sie sicher vor

Wenn also die thread.stop-Methode nicht aufgerufen wird, wie können wir den Thread dann sicher beenden?

Die sogenannte Sicherheit bedeutet, dass die Logik im Thread vollständig und nicht zur Hälfte ausgeführt werden muss.

Um diesen Effekt zu erzielen, stellt uns Thread drei ähnliche Methoden zur Verfügung: Interrupt, Interrupted und IsInterrupted.

interrupt dient zum Setzen des Interrupt-Flags für den unterbrochenen Thread; zum Erkennen des Interrupts und zum Löschen des Interrupt-Status. isInterrupted erkennt nur den Interrupt. Ein weiterer wichtiger Punkt ist, dass interrupted eine Klassenmethode ist, die auf den aktuellen Thread einwirkt, und isInterrupted auf diesen Thread einwirkt, also auf den Thread, der durch die Instanz dargestellt wird, die diese Methode im Code aufruft.

interrupt ist die Interrupt-Methode. Ihr Arbeitsablauf ist wie folgt:

  • Wenn die aktuelle Thread-Instanz wait() der Object-Klasse aufruft Die Methode „wait (long)“ oder „wait(long, int)“ oder die Methode „join()“, „join(long), „join(long, int)“ oder „Thread.sleep(long)“ oder „Thread.sleep(long, int)“ wurde aufgerufen Wenn sich die Methode dieser Instanz im blockierenden Zustand befindet, wird ihr Interrupt-Status gelöscht und eine InterruptedException empfangen.

  • Wenn dieser Thread während einer E/A-Operation auf dem InterruptibleChannel blockiert wird, wird der Kanal geschlossen und der Interrupt-Status des Threads wird auf true gesetzt, und Der Thread erhält eine java.nio.channels.ClosedByInterruptException-Ausnahme.

  • Wenn dieser Thread in java.nio.channels.Selector blockiert ist, wird der Interrupt-Status des Threads auf true gesetzt und er beginnt sofort mit Returned while Vorgang auswählen.

  • Wenn keine der oben genannten Bedingungen zutrifft, setzen Sie den Interrupt-Status auf „True“.

Im obigen Beispiel haben wir in der raiseNumber-Methode von NumberCounter die Thread.sleep-Methode aufgerufen. Wenn also zu diesem Zeitpunkt die Interrupt-Methode des Threads aufgerufen wird, wird die Thread Eine InterruptedException wird ausgelöst.

Wir ändern das obige Aufrufbeispiel wie folgt:

    public static void main(String[] args) throws InterruptedException {
        NumberCounter numberCounter = new NumberCounter();

        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    numberCounter.increaseNumber();
                } catch (InterruptedException e) {
                    System.out.println("捕获InterruptedException");
                    throw new RuntimeException(e);
                }
            }
        });

        thread.start();
        Thread.sleep(500);
        thread.interrupt();
        numberCounter.increaseNumber();
    }

Versuchen Sie es nach dem Ausführen erneut:

Exception in thread "main" Exception in thread "Thread-0" java.lang.RuntimeException: 逻辑不完整,数字计数器未执行完毕
    at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
    at com.flydean.Main2.main(Main2.java:21)
java.lang.RuntimeException: java.lang.thread.interrupt: sleep interrupted
    at com.flydean.Main2.lambda$main$0(Main2.java:13)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.InterruptedException: sleep interrupted
    at java.base/java.lang.Thread.sleep(Native Method)
    at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:17)
    at com.flydean.Main2.lambda$main$0(Main2.java:10)
    ... 1 more
捕获InterruptedException

可以看到,我们捕获到了这个InterruptedException,并且得知具体的原因是sleep interrupted。

捕获异常之后的处理

从上面的分析可以得知,thread.stop跟thread.interrupt的表现机制是不一样的。thread.stop属于悄悄终止,我们程序不知道,所以会导致数据不一致,从而产生一些未知的异常。

而thread.interrupt会显示的抛出InterruptedException,当我们捕捉到这个异常的时候,我们就知道线程里面的逻辑在执行的过程中受到了外部作用的干扰,那么我们就可以执行一些数据恢复或者数据校验的动作。

在上面的代码中,我们是捕获到了这个异常,打印出异常日志,然后向上抛出一个RuntimeException。

正常情况下我们是需要在捕获异常之后,进行一些处理。

那么自己处理完这个异常之后,是不是就完美了呢?

答案是否定的。

因为如果我们自己处理了这个InterruptedException, 那么程序中其他部分如果有依赖这个InterruptedException的话,就可能会出现数据不一致的情况。

所以我们在自己处理完InterruptedException之后,还需要再次抛出这个异常。

怎么抛出InterruptedException异常呢?

有两种方式,第一种就是在调用Thread.interrupted()清除了中断标志之后立即抛出:

   if (Thread.interrupted())  // Clears interrupted status!
       throw new InterruptedException();

还有一种方式就是,在捕获异常之后,调用Thread.currentThread().interrupt()再次中断线程。

public void run () {
  try {
    while (true) {
      // do stuff
    }
  }catch (InterruptedException e) {
    LOGGER.log(Level.WARN, "Interrupted!", e);
    // Restore interrupted state...
    Thread.currentThread().interrupt();
  }
}

这两种方式都能达到预想的效果。

Das obige ist der detaillierte Inhalt vonSo beenden Sie einen Thread in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen