在开始之前,我们先来看以下代码会有什么问题?
public class ThreadStopExample { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { try { System.out.println("子线程开始执行"); // 模拟业务处理 Thread.sleep(1000); } catch (Exception e) { } // 伪代码:重要的业务方法 System.out.println("子线程的重要业务方法"); }); t1.start(); // 让子线程先运行一点业务 Thread.sleep(100); // 终止子线程 t1.stop(); // 等待一段时间,确保子线程“执行完” Thread.sleep(3000); System.out.println("主线程执行完成"); } }
或许你已经发现了,上面这段代码使用了 Thread.stop() 来终止线程,在 Java 程序中是不允许这样终止线程的。什么?你问为什么不能这样?
首先来说 IDE 都会鄙视你了,它会阻止你使用 Thread.stop() !
什么?你不信。那么来看这张图:
好吧,那为什么不能这样用呢?总得给我一个敷衍的理由吧?
问题一:破坏了程序的完整性
其实是这样的,以文章刚开头的那段代码来说,它的执行结果是:
子线程开始执行
主线程执行完成
我们发现了一个惊天的大问题,最重要的那段伪代码竟然没执行,如下图所示:
可以看出使用 stop() 终止线程之后,线程剩余的部分代码会放弃执行,这样会造成严重的且不易被发现的惊天大 Bug,假如没有执行的那段代码是释放系统资源的代码,或者是此程序的主要逻辑处理代码。这就破坏了程序基本逻辑的完整性,导致意想不到的问题发生,而且它还很隐秘,不易被发现和修复。
有人说,这还不简单,我加个 finally 不就完了吗?
这???杠精哪都有,今年特别多。
行,既然这个说服不了你,咱接着往下看。
问题二:破坏了原子逻辑
我们知道在 Java 中 synchronized 属于独占式可重入悲观锁,如果我们使用它修饰代码,妥妥的多线程没问题,但如果碰到 stop() 方法就不一定了,直接来看代码吧。
public class ThreadStopExample { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); Thread t2 = new Thread(myThread); // 开启线程 t2.start(); for (int i = 0; i < 10; i++) { Thread t = new Thread(myThread); t.start(); } // 结束线程 t2.stop(); } /** * 自定义原子测试线程 */ static class MyThread implements Runnable { // 计数器 int num = 0; @Override public void run() { // 同步代码块,保证原子操作 synchronized (MyThread.class) { // 自增 num++; try { // 线程休眠 0.1 秒 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // 自减 num--; System.out.println(Thread.currentThread().getName() + " | num=" + num); } } } }
以上程序的执行结果为:
Thread-5 | num=1
Thread-4 | num=1
Thread-2 | num=1
Thread-1 | num=1
Thread-8 | num=1
Thread-6 | num=1
Thread-9 | num=1
Thread-3 | num=1
Thread-7 | num=1
Thread-10 | num=1
从结果可以看出,以上代码经过 synchronized 修饰的 ++ 和 -- 操作,到最后打印的结果 num 竟然不是 0,而是 1。