前言
接上章“线程的阻塞”,我们继续讲线程的生命周期中最后一个状态“销毁”
销毁(Dead)
线程会以如下3种方式结束,结束后就处于死亡状态:
- run()或call()方法执行完成,线程正常结束
- 线程抛出一个未捕获的Exception或Error
- 直接调用该线程stop()(已废弃,不推荐使用)方法来结束该线程,
该方法容易导致死锁,通常不推荐使用
线程一旦死亡,就不能复生
.
如果在一个死去的线程上调用start()
方法,会抛出
java.lang.IllegalThreadStateException
stop()被废弃的原因
补充下stop()
被废弃的原因,
因为它不安全,它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止
假如一个线程正在执行:
synchronized void {
x = 3;
y = 4;
}
由于方法是同步的
,多个线程访问时总能保证x,y被同时
赋值,
而如果一个线程正在执行到x = 3;时,被调用了 stop()
方法,即使在同步块中,它也干脆地stop
了,这样就产生了不完整的残废数据( 也就是只有x=3被执行了 )
安全的结束线程
线程属于一次性消耗品
,在执行完run()
方法之后线程便会正常结束了,
但有时我们需要线程执行循环处理任务
,
在这种情况下,一般是将这些任务放在一个死循环中
,如while循环
,
当需要结束线程时,如何退出线程呢?
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit
这些终止线程运行的方法已经被废弃了,使用它们是不安全的!想要安全有效的结束一个线程,可以使用下面的方法.
- 正常执行完run方法,然后结束掉;
- 使用退出标识符终止线程
- 使用interrupt()方法中断当前线程
正常执行的我们自然不用讲,下面举个例子
使用退出标志
终止线程
public class Test {
public static void main(String[] args) throws Exception {
Thread stopThread = new Thread(new Runnable(){
int i=0;
@Override
public void run() {
while (true) {
if(i==5)
break;
i++;
System.out.println(i);
}
}
});
stopThread.start();
}
}
使用标识符
来结束一个线程,是一个不错的方法,但其也有弊端
如果该线程是处于sleep、wait、join的状态时候,while循环就不会执行
那么我们的标识符就无用武之地了,当然也不能再通过它来结束处于这3种状态的线程了.
所以,此时可以使用interrupt
这个巧妙的方式结束掉这个线程.我们先来看看sleep、wait、join
方法的声明:
public final void wait() throws InterruptedException
public static native void sleep(long millis) throws InterruptedException
public final void join() throws InterruptedException
可以看到,这三者有一个共同点,
都抛出了一个InterruptedException的异常
在什么时候会产生这样一个异常呢?
每个Thread都有一个中断状状态,默认为false.
可以通过Thread对象的isInterrupted()方法来判断该线程的中断状态.
可以通过Thread对象的interrupt()方法将中断状态设置为true.
当一个线程处于sleep、wait、join这三种状态之一的时候,
如果此时他的中断状态为true,那么它就会抛出一个InterruptedException的异常,并将中断状态重新设置为false.
举个简单的例子验证下以上的特性
class StopThread extends Thread {
int i = 1;
@Override
public void run() {
while (true) {
System.out.println("计数器: "+i);
System.out.println("Interrupted: "+this.isInterrupted());
try {
System.out.println("线程将睡眠5s");
Thread.sleep(5000);
this.interrupt();
} catch (InterruptedException e) {
System.out.println("异常捕获,Interrupted: " + this.isInterrupted());
return;
}
i++;
}
}
}
public class Test {
public static void main(final String[] args) throws Exception {
StopThread stopThread = new StopThread();
stopThread.start();
}
}
结果
计数器: 1
Interrupted: false
线程将睡眠5s
计数器: 2
Interrupted: true
线程将睡眠5s
异常捕获了false
可以看到,在第一次循环中,睡眠5秒
,然后将中断状态
设置为true
.当进入到第二次循环的时候,中断状态就是第一次设置的true
,当它再次进入sleep
的时候,马上就抛出了InterruptedException
异常,然后被我们捕获了.然后中断状态又被重新自动设置为false
了.
由此,我们可以用interrupt
来优雅的结束掉线程
class StopThread extends Thread {
int i = 1;
@Override
public void run() {
while (true) {
System.out.println("计数器: "+i);
System.out.println("Interrupted: "+this.isInterrupted());
try {
System.out.println("线程将睡眠5s");
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("异常捕获,Interrupted: " + this.isInterrupted());
return;
}
i++;
}
}
}
public class Test {
public static void main(final String[] args) throws Exception {
StopThread stopThread = new StopThread();
stopThread.start();
Thread.sleep(2000);
stopThread.interrupt();
}
}
执行结果可能有两种
情况1
计数器: 1
Interrupted: false
线程将睡眠5s
异常捕获,Interrupted: false
子线程start()
,主线程睡眠2秒醒来后,调用子线程的interrupt()
,此时,子线程正好在sleep(2000)
,所以
循环第一次执行就结束了.
情况2
计数器: 1
Interrupted: false
线程将睡眠5s
计数器: 2
Interrupted: true
线程将睡眠5s
异常捕获,Interrupted: false
子线程start()
,主线程睡眠2秒醒来后,调用子线程的interrupt()
,此时子线程还没有处于sleep状态.然后再第2次while循环的时候,再次进入sleep状态,立马抛出InterruptedException
异常
最后,说明下stop()
和interrupt()
的区别
stop()调用后线程直接“强制停止”,不会等到run()方法执行完
interrupt()则会“优雅停止”,等到run()方法执行完再停止
欢迎关注我
技术公众号 “CTO技术”
网友评论