停止线程在多线程开发时是很重要的技术点。掌握此技术点可以对线程的停止进行有效的处理。停止线程并不像break语句那样干脆,需要一些技巧性的处理。
使用Java内置支持多线程的类设计多线程应用是很常见的事情,然而,多线程给开发人员带来一些新的挑战,如果处理不好就会导致超出预期的行为并且难以定位错误。
如何更好的停止一个线程?停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。听起来很简单,但是必须组好防范措施,以便达到预期效果,停止一个线程可以使用Thread.stop方法,但是最好不用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是很不安全的(unsafe)。而且是已经被弃用作废的方法(deprecated)。
大多数停止一个线程的操作使用Thread.interrupt方法,尽管方法的名称是停止终止的意思,但是这个方法不会终止一个正在运行的线程,还需要加入一些判断才可以完成一个线程的终止。后面会详细介绍。
在Java中有以下3种方法可以终止正在运行的线程:
1)使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2)使用stop方法强行终止线程,但是强烈不推荐这么做,以为stop和suspend以及resume一样,都是作废的过期方法,使用它们可能产生不可预料的结果
3)使用interrupt方法中断线程
停止不了的线程
下面示例将使用interrupt方法停止线程,但是这个方法并不想for+break那样马上停止循环,调用interrupt方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。下面看代码:
从运行结果来看,如果线程一直处于运行状态,interrupt方法并没有停止线程,如何停止线程呢?
判断线程是否是停止状态
在介绍如何停止线程前,先看一下如何判断线程的状态是不是停止的,在Thread中有以下两种方法,
1)this.interrupted:测试当前线程是否已经中断
2)this.isInterrupted:测试线程是否已经中断
源代码如下:
这两个方法有什么区别呢?先看this.interrupted:测试当前线程是否已经中断,当前线程是指运行this.interrupted方法的线程,为了更深入的理解,看下面代码:
代码中虽然是在demo上调用了interrupt方法来停止对象所代表的线程,在后面又使用demo.interrupted()来判断demo对象所代表的线程是否停止,但是从打印结果来看,线程并未终止,这也证明了interrupted方法的解释,测试当前线程是否已经中断。这个当前线程是mian,它从未中断过,所以打印的结果是两个false。
如何使main线程产生中断效果呢?看代码:
从结果显示主线程已是停止状态。方法interrupted的确判断出当前线程是否是停止状态。但为什么第二个是false?官方文档是这样解释的:
测试当前线程是否已经中断。线程的中断状态由该方法清除。换句话说,如果连续调用两次该方法,则第二次返回false(在第一次调用已经清除了其中断状态之后,且第二次调用检查完中断状态前,当前线程再次中断的情况除外)。
文档已经解释的很详细,interrupted方法具有清除状态的功能,所以第二次调用返回false。
再来看一下isInterrupted方法,上面的源码可以看到方法不是静态的,下面看一下示例:
运行结果:
从结果可以看出,方法isInterrupted并未清除状态标志,所以打印了两个true。最后再看一下两个方法的解释:
1)this.interrupted,测试当前线程是否已经是中断状态,执行后具有将状态标志清除为fasle的功能
2)this.isInterrupted,测试线程Thread对象是否已经是中断状态,但不清楚状态标志
能停止的线程---异常法
有了前面两个知识点,就可以在线程执行的代码中,判断一下线程是否是停止状态,如果是停止状态,则后面的代码不再运行即可。看例子:
上面的示例成功停止了for循环。但是如果循环体下面还有代码,还是会被执行,。比如
如何解决这个问题呢?我们更新一下代码:
运行结果:
可以看到for下面的没有执行,这就是异常中断法。
在沉睡中停止
如果线程在sleep状态下停止,会有什么效果呢?看代码:
运行结果:
从结果看,如果在sleep中停止一个线程,会进入catch语句,并且清除停止状态值,使之变成false。
上面的代码是先sleep然后再嗲用interrupt停止,与之相反的操作也要注意:
能停止的线程---暴力停止
使用stop方法停止线程是非常暴力的,例如:
运行结果:
方法stop与java.lang.ThreadDeath异常
调用stop方法会抛出ThreadDeath异常,但是通常情况下,不需要显式捕获。例如:
方法stop已经被废弃,因为如果强制让线程停止则有可能使一些请理性的工作得不到完成,另一种情况就是对锁定的对象进行了解锁,导致数据得不到同步处理,出现不一致的情况。
释放锁的不良后果
使用stop停止线程释放锁,会给数据造成不一致的结果,程序处理的数据就可能遭到破坏,最终导致程序错误,一定要注意。
看一个示例:
运行结果:
由于stop方法在jdk中已经标明是过期作废的方法,显然它在功能上有缺陷,所以不建议在程序中使用stop方法。
使用return停止线程
将interrupt方法与return结合,也能实现停止线程的效果。
例如:
运行结果:
不过还是建议使用抛异常的方法实现线程的停止,因为catch中可以对异常信息进行相关的处理,而且使用异常性流能更好更方便的控制程序的运行流程,不至于代码中出现很多return造成污染。