Skip to content

线程中断

如果一个线程需要执行一个长时间任务,就有可能需要在某一时刻中断线程。中断线程就是其他线程给当前线程发一个信号,当前线程收到信号后结束执行 run(),使得自身线程立刻结束。

例如,从网络下载 1G 文件,网速贼慢,用户等不及了就点击“取消”按钮,这时候程序就中断下载。

interrupt 方法

中断线程只需要调用目标线程的 interrupt() 方法,目标线程需要反复检测自身状态是否是 interrupted 状态,如果是就立刻结束运行。

java
public static void main(String[] args) throws InterruptedException {
  MyThread t1 = new MyThread();
  t1.start();

  Thread.sleep(10);
  t1.interrupt(); //10毫秒之后终止线程
  t1.join();
  System.out.println("end...");
}

class MyThread extends Thread {
  @Override
  public void run() {
    int n = 0;
    while (!isInterrupted()) {
      n++;
      System.out.println(n + " hello!");
    }
  }
}

需要注意,interrupt() 方法只是发出了“中断请求”,至于线程 t1 是否能立刻中断,需要看具体代码,上面的代码会立刻终止。

volatile 标识

终止线程的另一个方法是设置标识位。

通常使用一个 running 变量来标识线程是否应该继续运行,外部线程中,通过修改当前线程的 running = false ,来表示终止线程。

java
public static void main(String[] args) throws InterruptedException {
  MyThread t1 = new MyThread();
  t1.start();
  
  Thread.sleep(10);
  t1.running = false;
  System.out.println("end...");
}

class MyThread extends Thread {
  public volatile boolean running = true;

  @Override
  public void run() {
    int n = 0;
    while (running) {
      n++;
      System.out.println(n + " hello!");
    }
  }
}

注意到上面 MyThread 的标识位 running 使用了 volatile 关键字进行修饰,这表示 running 变量是线程间共享的变量。

为什么线程间共享变量需要使用 volatile 修饰?

答:在 java 虚拟机中,变量的值保存在主内存中,当线程访问变量时,它会先获取一个副本,并保存在自己的工作内存中,如果线程修改了变量的值,虚拟机 会在之后的某个时刻把值写到主内存,但这个时间并不确定。

这就导致,主内存变量 a = true,线程 A 执行 a = false 时,线程 A 只是把变量 a 的副本变为了 false,主内存的 a 还是 true,在 JVM 在把副本的 a 变量写回主内存之前,其他线程读取到的值还是 true,这就造成了很多线程之间的共享变量不一致。

总结

因此,volatile 关键字的目的是告诉虚拟机:

  • 每次访问变量时,总是获取主内存的最新值;
  • 每次修改变量后,立刻写回到主内存;

Released under the MIT License.