Java线程终止
Java 线程终止大体分为三种stop() / interrupt() / 标志位, 那么这三种的使用场景以及区别是怎样的呢?下面分析一下:
这个方法通俗易懂,stop直接停止就完事儿了,但是细心的同学可能会注意到这个stop()函数已经被弃用了Thread.stop(),一般情况下既然弃用了我们不用就完了,但是最好还是要了解一下问什么会弃用这么一个让人眼前一亮的函数。通过一段简短的Demo就会清晰明了:
public class StopThread extends Thread {
private int i = 0, j = 0;
@Override
public void run() {
synchronized (this) {
// 增加同步锁,确保线程安全
++i;
try {
// 休眠10秒,模拟耗时操作
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("线程已经终止!");
e.printStackTrace();
}
++j;
}
}
/**
* 打印i和j
*/
public void print() {
System.out.println("i=" + i + " j=" + j);
}
}
这个是线程run()方法业务实现,很明显,简单的执行++i 和 ++j 操作,然后自定义print()方法打印 i、j 的值,就这么单纯。那线程执行入口在哪呢,来看下面:
public class Demo3 {
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
// 休眠1秒,确保i变量自增成功
Thread.sleep(1000);
// 暂停线程
thread.stop(); // 错误的终止
// thread.interrupt(); // 正确终止
while (thread.isAlive()) {
// 确保线程已经终止
} // 输出结果
thread.print();
}
}
这块还是一如既往的单纯,创建StopThread 对象并执行,然后在执行中调用Thread.stop()函数终止,最后调用thread.print()查看执行结果。
此时看到 i=1 j=0 , 显然产生了原子性问题,破坏了线程安全,所以这样一个终止方法还能用到哪呢?弃用明智。
-
Thread.interrupt()
还是Demo3中代码中,应该看到了有一个thread.interrupt()函数调用,此时打开注释再次验证:
结果打印 i = 1 j=1, 保证了原子性,并没有产生安全性问题,但是抛出了 InterruptedException异常,首先先说结论:Interrupt 就是通过抛出异常来中断线程,我们可以再try/catch() 中捕获异常做特殊业务处理。接下来为什么抛出异常呢?我将源码放到这:
/**
* Interrupts this thread.
*
* 除非当前线程正在中断自身
*总是允许,{@link #checkAccess() checkAccess}方法
*调用该线程,可能会导致一个SecurityException被抛出。
*
* 如果这个线程在{@link的调用中被阻塞
* *对象#wait() wait()}, {@link对象#wait(long) wait(long)},或{@link
* *对象#wait(long, int) wait(long, int)}方法的{@link对象}
* 类或{@link #join()}、{@link #join(long)}、{@link
* * #加入(长,int)}, {@link #睡眠(长)}或{@link #睡眠(长, int)},
* *这个类的方法,那么它的中断状态将被清除
* 将收到一个{@link InterruptedException}。
*
* 如果这个线程在{@link的I/O操作中被阻塞
* * java.nio.channels。InterruptibleChannel InterruptibleChannel}
* 然后通道将被关闭,线程的中断
* 将设置状态,线程将收到一个{@link
* * java.nio.channels.ClosedByInterruptException}。
*
* 如果这个线程在{@link java.nio.channels.Selector}中被阻塞
* 然后线程的中断状态将被设置,它将返回
* *立即从选择操作,可能与非零
* 值,就像选择器的{@link一样
* * java.nio.channels。调用了Selector#wakeup wakeup}方法。
*
* 如果前面的条件都不成立,那么这个线程就是中断的
* 将设置状态。
*
* 中断非活动的线程不一定会有任何影响。
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
public class Demo4 extends Thread {
public volatile static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
while (flag) { // 判断是否运行
System.out.println("运行中");
Thread.sleep(1000L);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 3秒之后,将状态标志改为False,代表不继续运行
Thread.sleep(3000L);
flag = false;
System.out.println("程序运行结束");
}
}
可以看到通过 volatile 关键字定义了一个flag 静态变量,在线程的while(flag)来判定条件是否成立来执行任务,因此就可以操作 flag = false; 来停止线程执行。
因此,推荐 Thread.interrupt() / 标志位 来作为常用的终止线程的方法。