停止线程是在多线程开发时很重要的技术点,掌握此技术可以对线程的停止进行有效的处理。
停止不了的线程
调用interrupt()方法来停止线程,但interrupt()方法的使用效果并不像for-break语句那样,马上就停止循环。调用interrupt()方法仅仅是当前线程中打了一个停止的标记,并不是真正的停止线程。
代码清单1 interrupt()方法没有真正停止线程
从运行结果看,调用interrupt方法并没有停止线程。如何停止线程呢?
图1 控制台输出50万行的日志
判断线程是否是停止状态
判断线程是不是停止,Thread.java类里提供了两种方法。
1)this.interrupted():测试当前线程是否已经中断,执行后具有将状态标志置清楚为false()的功能。其声明为:
2)this.isInterrupted():测试线程Thread对象是否已经中断,但不清楚状态标志。其声明为:
而isInterrupted(Boolean ClearInterrupted)是一个本地方法,private native boolean isInterrupted(boolean ClearInterrupted);
那么这两个方法有什么区别呢?
a. this.interrupted()方法的解释就是测试当前的线程是否中断,当前线程是指运行this.interrupted()方法的线程。
代码清单2 测试interrupted()方法
图2 thread.interrupted()代表mian线程
b. 如何使main线程产生中断效果呢?
代码清单3 mian线程中断
图3 主线程mian已是停止
从上述结果看,方法interrupted()的确判断出当前线程是否停止状态。但为什么第二个布尔值是false呢?查看一下官方帮助文档对interrupted方法的解释。
测试当前线程是否中断。线程的中断状态由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。
文档已经解释很详细,interrupted()方法具有清除状态的功能,所以第2次调用interrupted()方法返回的值是false。
c. 介绍完interrupted()方法后再来看一下isInterrupted()方法。
代码清单4 isInterrupted()方法
从结果中可以看到,方法isInterrupted()并未清除状态标志,所以打印两个true。
图4 已经是停止状态
能停止的线程–异常法
1) 在线程中用for语句来判断,线程是否是停止状态,如果是停止状态,则后面的代码不再运行即可。
代码清单5 break停止线程
图5 线程已经是停止状态
2) 上面的示例虽然停止了线程,但如果for语句下面还有语句,还是会继续运行的。
代码清单5中,去掉System.out.println("我被输出,如果此代码时for又继续运行,线程并未停止!");
的注释。运行结果如图6所示。
图6 for后面的语句继续运行
3) 对于语句继续运行的问题,该怎么解决呢?
建议使用该方法来实现线程停止!
代码清单6 抛出错误InterruptedException
运行结果如下,for下面语句将不再进行运行。
图7 运行结果
在沉睡中停止
1) 如果线程在sleep()状态下停止线程,会是什么效果呢?
代码清单7 sleep()状态下停止线程
程序运行后效果如图8所示。如果在sleep状态下停止某一线程,会进入catch语句,并且清除停止状态值,使之变成false。
图8 运行结果
2)前一个实验是先sleep然后再用interrupt()停止,与之相反的操作在学习线程时也要注意。
代码清单7 interrupt状态下sleep线程
控制台输出如下两图所示。
图9 先执行interrupt停止线程
图10 遇到sleep提示异常
能停止的线程–暴力停止
使用stop()方法停止线程则是非常暴力的。
代码清单8 stop停止线程
线程被暴力停止(stop),运行结果如下。
图11 线程被暴力停止
方法stop()与java.lang.ThreadDeath异常
调用stop()方法时会抛出java.lang.ThreadDeath异常,但在通常情况下,此异常不需要显式地辅捉。
代码清单9 抛出java.lang.ThreadDeath异常
方法stop()已经作废,因为如果强制让线程停止则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。
图12 进入catch异常
释放锁的不良后果
使用stop()释放锁将会给数据造成不一致的结果。如果出现这样的情况,程序处理的数据集有可能遭到破坏,最终导致程序执行的流程错误,一定要特别注意。
代码清单10 stop()破坏数据一致
由于stop()方法已经在JDK中被标明“作废/过期”的方法,显然它在功能上具有缺陷,所以不建议在程序中使用stop()方法。
图13 强制stop造成数据不一致
使用return停止线程
将方法interrupt()与return结合使用也能实现停止线程的效果。
代码清单11 return停止线程
不过还是建议使用“抛异常”的方法来实现线程的停止,因为在catch块中还可以将异常先上抛,使线程停止的事件得以传播。
图14 成功停止运行