线程中断
如果一个线程需要执行一个长时间任务,就有可能需要在某一时刻中断线程。中断线程就是其他线程给当前线程发一个信号,当前线程收到信号后结束执行 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
关键字的目的是告诉虚拟机:
- 每次访问变量时,总是获取主内存的最新值;
- 每次修改变量后,立刻写回到主内存;