线程通过start()方法启动后,会在run()方法执行结束后进入终止状态。
stop()方法终止线程会导致的两个问题
- 立即抛出ThreadDeath异常,在run()方法中任何一个执行指令都可能抛出ThreadDeath异常。
- 会释放当前线程所持有的所有锁,这种锁的释放是不控的。
示例代码
/**
* stop()方法会出现的问题
* @author ZhangHao
* @since 1.0.0
*/
public class ThreadStopExample extends Thread {
public void run(){
try {
for (int i = 0; i < 100000; i++){
System.out.println("Running.." + i);
}
System.out.println("the code that it will be executed");
}catch (Throwable e){
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new ThreadStopExample();
thread.start();
Thread.sleep(100);
thread.stop();
}
}
//运行结果
Running..21584
Running..21585
Running..21586
Running..21587
Running..21588java.lang.ThreadDeath
at java.lang.Thread.stop(Thread.java:853)
at ThreadStopExample.main(ThreadStopExample.java:21)
运行结果发现问题
- 在run()方法中,代码System.out.println("the code that it will be executed");还没有执行,就因为ThreadDeath异常导致线程中断了,造成业务处理的不完整性。
- 观察内容Running..21588java.lang.ThreadDeath,我们使用的是println()方法,但是这里并没有换行,原因如下:
//println()方法的源码
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
*println()方法包含两个操作,一个操作是输出print(x),另一个操作是换行,为了保证两个操作的原子性,
增加了synchronized同步锁,理论上说不应该出问题。但是stop()方法会释放同步锁,使得这两个操作不是
原子的,从而导致了newLine()方法还没执行就被线程中断了。因此在实际应用中,一定不能使用stop()方法
来中断线程。
*/
Interrupt安全中断线程
在Thread中提供了一个interrupt()方法,用来向指定线程发送中断信号,收到该信号的线程可以使用isInterrupted()方法来判断是否被中断。
/**
* 线程中断的例子
* @author ZhangHao
* @since 1.0.0
*/
public class InterruptExample extends Thread{
public void run(){
int i = 0;
while (!Thread.currentThread().isInterrupted()){
i++;
}
System.out.println("线程已被中断,i=" + i);
}
public static void main(String[] args) throws InterruptedException {
InterruptExample interruptExample = new InterruptExample();
interruptExample.start();
TimeUnit.SECONDS.sleep(1);
System.out.println(interruptExample.isInterrupted());
interruptExample.interrupt();
System.out.println(interruptExample.isInterrupted());
}
}
//运行结果
false
true
线程已被中断,i=1523199841
interrupt()方法并没有武断的把运行中的线程停止,而是通过传递标识的方式让运行的线程自己决定是否停止。这意味着该线程在收到该信号后,可以继续把run()方法中的指令运行完成,最后让run()方法安全执行结束,完成线程的中断功能。
中断处于阻塞状态下的线程
如果线程因为sleep()、Object.wait()等方法阻塞,而其他线程想通过interrupt()方法对该线程进行中断,那么这个线程必须先被唤醒,否则无法响应中断信号。
/**
* @author ZhangHao
* @since 1.0.0
*/
public class BlockedThreadInterruptExample extends Thread{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
try {
TimeUnit.SECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt(); //加上这行代码才能真正中断阻塞线性不加这行代码程序是一直运行的状态说明线程并没有真正的被中断。
}
System.out.println("线程被中断");
}
}
public static void main(String[] args) throws InterruptedException {
BlockedThreadInterruptExample blockedThreadInterruptExample = new BlockedThreadInterruptExample();
blockedThreadInterruptExample.start();
TimeUnit.MICROSECONDS.sleep(100);
System.out.println(blockedThreadInterruptExample.isInterrupted());
blockedThreadInterruptExample.isInterrupted();
System.out.println(blockedThreadInterruptExample.isInterrupted());
}
}
//运行结果
false
true
线程被中断
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at BlockedThreadInterruptExample.run(BlockedThreadInterruptExample.java:12)