Heim  >  Artikel  >  Java  >  Detaillierte Einführung in die Thread-Unterbrechung in Java

Detaillierte Einführung in die Thread-Unterbrechung in Java

WBOY
WBOYnach vorne
2022-11-09 13:56:441199Durchsuche

Dieser Artikel vermittelt Ihnen relevantes Wissen über Java und stellt hauptsächlich den relevanten Inhalt zur Thread-Unterbrechung vor. Dies ist eine Möglichkeit, sich gegenseitig zwischen Threads zu unterbrechen, z. B. zwischen zwei Threads a und b, wenn a die Ausführung von b unterbrechen möchte Zu einem bestimmten Zeitpunkt können Sie die Methode b.interrupt () aufrufen, um sie zu unterbrechen. Ich hoffe, dass sie für alle hilfreich ist.

Detaillierte Einführung in die Thread-Unterbrechung in Java

Empfohlene Studie: „Java-Video-Tutorial

Thread-Unterbrechung in Java

1 Einführung in Thread-Unterbrechungsmethoden

interrupt()-Methode in der Java-Multithread-Programmierung, Die Methode isInterrupted() und die Methode interrupted() sind beide Methoden, die sich auf die Thread-Unterbrechung beziehen und sehr wichtig sind. Die Namen dieser drei Methoden sind sehr ähnlich und es ist leicht, sie zu verwechseln, wenn Sie die Prinzipien nicht verstehen. Sie werden hier vorgestellt, um sie zu unterscheiden. Da die interrupt()-Methode und die isInterrupted()-Methode beide Instanzmethoden sind (keine statischen Methoden für die Klasse), habe ich einen thread1 vorangestellt davon. code>, der einen instanziierten spezifischen Thread darstellt: <code>interrupt()方法、isInterrupted()方法和interrupted()方法都是跟线程中断相关的方法,都非常重要。这三个方法名称非常相似,不理解原理时容易混淆,这里分别介绍下,以加以区分。由于interrupt()方法和isInterrupted()方法都是实例方法(非类上的静态方法),因此我在前面加了个thread1,表示一个实例化的具体线程:

thread1.interrupt()方法

thread1.interrupt()方法用来中断线程,所谓的中断,大家可以通俗的理解为打断。比如有两个线程ab,当线程a因为某些原因想打断线程b时,a线程内部可以调用b.interrupt()。不过要注意,实现上是通过设置线程b中断状态标记实现的。b线程代码运行期间,可以在一个循环体内不断的判断该中断状态标记,以确认是否真正响应a的中断请求(比如退出执行等等)。

thread1.isInterrupted()方法

thread1.isInterrupted()方法用来获取一个线程的中断状态。比如有两个线程ab,当线程a因为某些原因想打断线程b时,可以通过b.interrupt()b进行中断。在线程b内部,可以判断自己的中断状态,是否是被中断的,然后根据中断状态确认是否响应中断请求(比如退出当前线程的循环体等等)。thread1.isInterrupted()方法内部直接调用了native方法,传入的ClearInterrupted参数是false,表示不清空中断状态标记:

public boolean isInterrupted() {
  return isInterrupted(false);
}// ClearInterrupted表示是否清楚中断状态标记private native boolean isInterrupted(boolean ClearInterrupted);

因此调用完该方法后,中断标志位不清除。

Thread.interrupted()方法

Thread.interrupted()是定义在Thread类上的静态方法,用来判断当前线程的中断状态,跟thread1.isInterrupted()不同的是,该方法返回中断状态之后,会复位(reset)中断状态标记,所谓的复位即恢复默认状态,也可以说是清空中断状态标记。看Thread类源码可以看到Thread.interrupted()方法实现非常简单,内部直接调用了native方法,只不过ClearInterrupted参数传的是true,表示清空中断状态标记:

public static boolean interrupted() {
  return currentThread().isInterrupted(true);
}// ClearInterrupted表示是否清楚中断状态标记private native boolean isInterrupted(boolean ClearInterrupted);

可以看出thread1.isInterrupted()Thread.interrupted()的区别其实就在于获取完中断状态标记之后,是否复位。大家可以根据需要进行选择使用。

2 不考虑线程阻塞时如何优雅的停止一个线程

要想优雅的停止某个线程的运行,需要前面介绍的中断机制。比如两个线程ab,当线程a想中断线程b时,可以通过调用b.interrupt()方法,来设置线程b的中断标记,以达到通知线程b的目的。而线程b需要不断的检查自己的中断标记,以随时响应其他线程的中断,比如下面的实现所示:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while(true) {
                    if (Thread.interrupted()) {
                        break;
                    }
                    System.out.println("thread b running, num is:" + num++);
                }
            }
        });
        b.start();
        // 主线程sleep 1ms,让线程b循环一小会
        Thread.sleep(1);
        // 中断线程b
        b.interrupt();
    }
}

这里在主线程内新创建了一个线程b,线程b内部是一个循环体,每次循环开始都检查一下中断状态,确认是否被中断,如果被中断了即退出循环,结束线程的执行。主线程sleep了1ms,让线程b先循环几次。接着主线程通过b.interrupt()对线程b

thread1.interrupt() method

thread1.interrupt( ) Die Methode wird verwendet, um den Thread zu unterbrechen. Die sogenannte Unterbrechung kann allgemein als interrupt verstanden werden. Beispielsweise gibt es zwei Threads a und b, wenn Thread a den Thread b aus irgendeinem Grund unterbrechen möchte , , b.interrupt() kann innerhalb des a-Threads aufgerufen werden. Bitte beachten Sie jedoch, dass die Implementierung durch Setzen des Interrupt-Status-Flags des Threads b erreicht wird. Während der Ausführung des b-Thread-Codes kann das Interrupt-Status-Flag kontinuierlich in einem Schleifenkörper beurteilt werden, um zu bestätigen, ob es tatsächlich auf die Interrupt-Anfrage von reagiert a (Zum Beispiel das Beenden der Ausführung usw.). 🎜

thread1.isInterrupted()-Methode

🎜thread1.isInterrupted()-Methode wird verwendet, um den Wert zu erhalten eines Thread-Interrupt-Status. Beispielsweise gibt es zwei Threads a und b, wenn Thread a den Thread b aus irgendeinem Grund unterbrechen möchte , , Sie können b durch b.interrupt() unterbrechen. Innerhalb des Threads b können Sie seinen Interrupt-Status ermitteln und feststellen, ob er unterbrochen wurde, und dann anhand des Interrupt-Status bestätigen, ob auf die Interrupt-Anfrage reagiert werden soll (z. B. Verlassen des Schleifenkörpers des aktuellen Threads, usw.). Die Methode thread1.isInterrupted() ruft direkt die Methode native auf und der im Parameter ClearInterrupted übergebene Parameter ist false. was bedeutet, dass das Interrupt-Status-Flag nicht gelöscht wird: 🎜
thread b running, num is:0thread b running, num is:1thread b running, num is:2...
thread b running, num is:25thread b running, num is:26thread b running, num is:27Process finished with exit code 0
🎜Nach dem Aufruf dieser Methode wird das Interrupt-Flag-Bit also nicht gelöscht. 🎜

Thread.interrupted()-Methode

🎜Thread.interrupted() ist in Thread definiert Statische Methode für die Klasse wird verwendet, um den Unterbrechungsstatus des aktuellen Threads zu bestimmen. Der Unterschied zu thread1.isInterrupted() besteht darin, dass diese Methode den zurückgibt Unterbrechungsstatus, Die Interrupt-Statusmarkierung wird zurückgesetzt (Reset). Der sogenannte Reset dient dazu, den Standardzustand wiederherzustellen, was auch als Löschen der Interrupt-Statusmarkierung bezeichnet werden kann. Wenn Sie sich den Quellcode der Klasse Thread ansehen, können Sie sehen, dass die Implementierung der Methode Thread.interrupted() sehr einfach ist wird intern direkt aufgerufen, aber Der ClearInterrupted-Parameter übergibt true, was bedeutet, dass die Interrupt-Statusmarkierung gelöscht wird: 🎜
public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while(true) {
                    if (Thread.interrupted()) {
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }
                    System.out.println("thread b running, num is:" + num++);
                }
            }
        });
        b.start();
        // 主线程sleep5.5秒,让线程b循环5次
        Thread.sleep(5500);
        // 中断线程b
        b.interrupt();
    }
}
🎜Sie können sehen, dass thread1.isInterrupted () und Thread Der Unterschied zwischen .interrupted() liegt tatsächlich darin, ob es nach Erhalt der Interrupt-Statusmarkierung zurückgesetzt werden soll. Sie können es je nach Bedarf verwenden. 🎜

2 So stoppen Sie einen Thread ordnungsgemäß, ohne Thread-Blockierung in Betracht zu ziehen🎜🎜Wenn Sie die Ausführung eines Threads ordnungsgemäß stoppen möchten, benötigen Sie den zuvor eingeführten Interrupt-Mechanismus. Beispielsweise gibt es zwei Threads a und b. Wenn Thread a Thread b unterbrechen möchte, kann dies geschehen Die Methode call >b.interrupt() wird verwendet, um das Interrupt-Flag von Thread b zu setzen, um Thread b zu benachrichtigen. Thread b muss ständig seine eigene Interrupt-Markierung überprüfen, um jederzeit auf Interrupts von anderen Threads reagieren zu können, wie in der folgenden Implementierung gezeigt: 🎜
thread b running, num is:0thread b running, num is:1thread b running, num is:2thread b running, num is:3thread b running, num is:4java.lang.InterruptedException: sleep interrupted
  at java.lang.Thread.sleep(Native Method)
  at test.TestMain.run(TestMain.java:25)
  at java.lang.Thread.run(Thread.java:748)
thread b running, num is:5thread b running, num is:6thread b running, num is:7thread b running, num is:8thread b running, num is:9...
🎜Hier ist etwas Neues im Hauptthread Ein Thread b wird erstellt. Innerhalb des Threads b befindet sich ein Schleifenkörper. Zu Beginn jeder Schleife wird der Interrupt-Status überprüft, um zu bestätigen, ob er unterbrochen wurde. Wenn es unterbrochen wird, beenden Sie die Schleife und beenden die Ausführung des Threads. Der Hauptthread schläft 1 ms lang und lässt Thread b zunächst mehrere Male durchlaufen. Dann unterbricht der Hauptthread den Thread b durch b.interrupt(). Wenn Sie das Programm ausführen, können Sie die Ausgabe wie folgt sehen (die Ergebnisse sind auf verschiedenen Computern unterschiedlich): 🎜
thread b running, num is:0thread b running, num is:1thread b running, num is:2...
thread b running, num is:25thread b running, num is:26thread b running, num is:27Process finished with exit code 0

可以看到主线程成功的中断了线程b。当然,主线程之所以能成功的中断线程b,是因为线程b一直在检查自己的中断状态(如果线程b太自我,不考虑其他线程,只考虑自己运行,那主线程就无法成功打断线程b了)。

3 考虑线程阻塞时如何优雅的停止一个线程

前面我们成功的在主线程中中断了线程b,然后如果线程b中存在阻塞,比如下面的代码所示,线程b在sleep时被主线程中断:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while(true) {
                    if (Thread.interrupted()) {
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }
                    System.out.println("thread b running, num is:" + num++);
                }
            }
        });
        b.start();
        // 主线程sleep5.5秒,让线程b循环5次
        Thread.sleep(5500);
        // 中断线程b
        b.interrupt();
    }
}

这时线程b会抛出InterruptedException异常,上面的代码中我们仅仅打印了下该异常,相当于什么都没做。运行该代码结果如下:

thread b running, num is:0thread b running, num is:1thread b running, num is:2thread b running, num is:3thread b running, num is:4java.lang.InterruptedException: sleep interrupted
  at java.lang.Thread.sleep(Native Method)
  at test.TestMain$1.run(TestMain.java:25)
  at java.lang.Thread.run(Thread.java:748)
thread b running, num is:5thread b running, num is:6thread b running, num is:7thread b running, num is:8thread b running, num is:9...

可以看出,主线程未能成功中断线程b

3.1 InterruptedException异常介绍

线程内调用waitjoinsleep时都会进入阻塞状态。当线程处于阻塞状态时被中断,这时线程就会抛出InterruptedException异常,其实大家可以通俗的理解为一种通知即可。以sleep方法为例,大家可以按如下模拟实现来理解(底层是native实现):

public static void sleep(long millis) throws InterruptedException 
{
    while (/* still waiting for millis to become zero */) 
    {
        if (Thread.interrupted())
        {
            throw new InterruptedException();
        }
        // Keep waiting
    }
}

有了InterruptedException异常通知,线程就可以在阻塞时立即知道被中断了,进而采取一定的措施响应中断。需要注意的一点是,由于抛出了InterruptedException异常,因此不会在设置中断标志位。

3.2 考虑线程阻塞时如何优雅的停止一个线程

理解了InterruptedException异常,我们就可以在线程即使发生阻塞时也能成功进行中断了,如下所示:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while(true) {
                    if (Thread.interrupted()) {
                        break;
                    }
                    try {
                        Thread.sleep(1000); // 用sleep来模拟线程的执行
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt(); // 注意这里是重点!
                    }
                    System.out.println("thread b running, num is:" + num++);
                }
            }
        });
        b.start();
        // 主线程sleep5.5秒,让线程b先循环5次
        Thread.sleep(5500);
        // 中断线程b
        b.interrupt();
    }
}

这里我们在检查到InterruptedException异常时,重新设置了中断标志位,这样下次循环一开始时,即可判断被中断了,进而退出循环体。当然我们可以在InterruptedException异常catch时直接退出。

4 总结

我们介绍了Java中的线程中断相关知识点,通俗来讲,大家可以理解为中断就是一种线程间相互打断的一种方式,比如两个线程aba如果在某一时刻想打断b的执行,则可以调用b.interrupt()方法进行中断,但是要注意,这里仅仅是设置b的中断状态位,b看到中断状态位后可以自行决定是否响应,当然,正常情况我们写的代码都需要做好中断状态位的判断(这一点大家在写业务代码时确实经常忽略)。另外对于阻塞中的线程,Java通过InterruptedException异常来进行通知。

推荐学习:《java视频教程

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in die Thread-Unterbrechung in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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