停止一个正在运行的java线程,主要有以下三种方式:
1. 已废弃的 Thread.stop()
stop() 的源码就不列了,这是一个自从jdk1.2开始就被废弃的方法。关于该方法的官方解释:
这种方法本质上是不安全的。停止一个线程导致它解锁它锁定的所有监视器(未检查的{@code ThreadDeath}异常向上传播堆栈的自然结果)。如果以前受这些监视器保护的任何对象处于不一致的状态,其他线程就可以看到损坏的对象,这可能会导致任意行为。
许多使用{@code stop}的代码应该被简单地修改一些变量来指示目标线程应该停止运行的代码所取代。目标线程应该定期检查这个变量,如果变量表示要停止运行,则从它的run方法中有序地返回。如果目标线程等待很长时间(例如,在条件变量上),则应该使用{@code interrupt}方法来中断等待。
第二段是官方给出的停止线程的解决方案,我们着重看第一段,第一段话解释了这个方法为什么不安全。
线程的监视器控制着线程中代码和变量的逻辑关系,假设线程的某个监视器监视着 a和b 这两个变量的逻辑关系,如a<b,如果某一时刻a=b,那此时变量的逻辑关系是受监视器保护的,若此时恰好收到一个stop命令,产生一个ThreadDeath错误,监视器被解锁,这就会导致逻辑错误。
ThreadDeath是个java.lang.Error,不是java.lang.Exception。
public class ThreadDeath extends Error {
private static final long serialVersionUID = -4417128565033088268L;
}
2. Thread.interrupt()
interrupt()作用是中断此线程,实际上只是给线程设置一个中断标志,不会中断或停止一个正在运行的线程。
如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的 interrupt() 方法,那么该线程就会被中断,并抛出一个 InterruptedException中断异常,线程仍会继续执行。
具体的实践方法之后会展示。
3. 共享变量
停止一个线程最好的方式就是让他执行完毕,虽然我们不能让线程立即停止,但能决定线程何时执行完毕。
通过条件变量控制线程的执行,线程内部检查变量状态,外部改变变量值可控制停止执行。为保证线程间的即时通信,需要使用volatile关键字或锁,确保读线程与写线程间变量状态一致。
写一个模板:
public class TestThread implements Runnable{
private volatile boolean flag = false;
public void stopThread(){
flag = true;
}
@Override
public void run() {
while (!flag){
// 业务代码
}
}
}
这只是在一般情况下停止线程,如果该线程处于阻塞状态,应结合第二点 interrupt() 来停止线程。
public class TestThread extends Thread{
private volatile boolean flag = false;
public void stopThread(){
flag = true;
interrupt(); // 解除中断状态
}
@Override
public void run() {
while (!flag){
try {
// 业务代码
}catch (InterruptedException e){
if (flag){
return;
}
continue;
}
}
}
}