Java中如何正确地中断一个线程?

本文主要整理了关于线程中断的相关知识点。

1.线程的状态

  • NEW (新建)
    一个尚未启动的线程处于这一状态。(A thread that has not yet started is in this state.)
  • RUNNABLE (可运行)
    一个正在 Java 虚拟机中执行的线程处于这一状态。(A thread executing in the Java virtual machine is in this state.)
  • BLOCKED (阻塞)
    一个正在阻塞等待一个监视器锁的线程处于这一状态。(A thread that is blocked waiting for a monitor lock is in this state.)
  • WAITING (等待)
    一个正在无限期等待另一个线程执行一个特别的动作的线程处于这一状态。(A thread that is waiting indefinitely for another thread to perform a particular action is in this state.)
  • TIMED_WAITING (计时等待)
    一个正在限时等待另一个线程执行一个动作的线程处于这一状态。(A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.)
  • TERMINATED (终止)
    一个已经退出的线程处于这一状态。(A thread that has exited is in this state.)

线程的状态转换图 来自@牛客网:

Java中如何正确地中断一个线程?_第1张图片

2.如何正确中断一个线程?

2.1 利用标志位。

这种简单地设置标志位来中断线程的方法,其弊端是当线程被阻塞时,没办法读到标志位,也中断不了线程。

public class TestThread extends Thread {   
    private volatile boolean finished = false;    
    public void stopThread() {        
        finished = true;
    }
    @Override    
    public void run() {        
        while (!finished) {          
            // do something
        }   
    }
}

2.2 调用Thread.interrupt()

interrupt()的本质也是利用了标志位来中断线程,它并不会真正地中断一个线程,而是通过改变标志位,让线程自己根据标志位和时机,灵活地决定要不要退出线程

关于中断线程,JDK提供了三个与之相关的方法,之前被废弃的方法这里就不多赘述。

2.2.1 public void interrupt()
public void interrupt()
中断线程。 
如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,
或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,
则其中断状态将被清除,它还将收到一个 InterruptedException。

所以调用interrupt()的时候分为两种情况:

  • 线程正常运行的情况(没有阻塞),那么该线程的中断标志位被设置为true,如果没有在线程中读取中断状态,退出线程里的循环的话,线程将继续执行。
  • 线程被阻塞的情况,由于线程已经被阻塞,要中断线程的话,就需要将线程的中断状态清除,同时抛出InterruptedException异常,线程才得以中断。
2.2.2 public static boolean interrupted()
public static boolean interrupted()
测试当前线程是否已经中断。线程的中断状态由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。 

interrupted(),首先会返回当前线程的中断状态,然后会将线程的中断状态清除,也就是将标志位设置为false。

2.2.3 public boolean isInterrupted()
public boolean isInterrupted()
测试线程是否已经中断。线程的中断状态不受该方法的影响。 

而isInterrupted(),只会返回线程中断状态,不会修改标志位。

这两者的差别 简单地说就是:

Thread.isInterrupted() 用来读取中断状态, Thread.interrupted() 用来读取中断状态和清除标志位。

tips:在线程执行完毕之后,线程的中断状态会被修改为false。

2.2.4 例子:
public class TestThread extends Thread {
     @Override
     public void run() {
          while (!isInterrupted()) {
                try {
                   // do something
                   
                    } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
          }
     }
}

TestThread testThread = new TestThread();
testThread.start();
// 一段时间以后
testThread.interrupt();

总的来说,当新开的线程被阻塞了,在调用interrupt()的时候,线程的中断状态清除,同时抛出InterruptedException异常,注意,这个时候中断状态被清除了!你需要在catch语句里面重新调用interrupt(),来维持中断状态,否则,由于中断状态被清除,当程序继续执行到while (!isInterrupted())的时候,线程是不会停下来的。

参考资料:

https://www.ibm.com/developerworks/cn/java/j-jtp05236.html

http://ibruce.info/2013/12/19/how-to-stop-a-java-thread/

你可能感兴趣的:(Java中如何正确地中断一个线程?)