停止线程
停止线程的方法有:
- 1.run() 方法执行完
- 2.线程对象调用stop() 方法
- 3.线程对象调用 interrupt()方法
- 4.使用共享变量 volatile
run()方法执行完,没什么说的, 线程调用stop(),这个方法已经被废弃(不建议使用)
原因:
-1. 即刻抛出ThreadDeath异常,在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中。
-2. 释放该线程所持有的所有的锁
主要是第二点,释放该线程所持有的所有的锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。
来源:link
先来一个简单的常用的安全的停止线程的方法
public class ThreadSafeMethodOne extends Thread {
/*volatile 保证 多个线程对同一变量的共享,
通俗点来说就是 保证任何一个线程在读取 volatile 修饰的域 的时候,都将看到的是最新修改的值
*/
public volatile boolean isExit = false;
@Override
public void run() {
super.run();
while (!isExit){
// todo
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程退出了");
}
}
测试方法:
public void testSafeOne(){
ThreadSafeMethodOne methodOne = new ThreadSafeMethodOne();
methodOne.start();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
methodOne.isExit = true;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
结果:
线程退出了
这里注意一点,如果主线程执行时间短,是不会看到这行字的打印的
再用interrupt()的方法
这个方法不是说 执行就立马停止线程,他只是设置了一个停止标识,系统看到这个标识,才会停止线程
public class ThreadSafeMethodTwo extends Thread {
@Override
public void run() {
super.run();
// while (!Thread.interrupted()) 和 isInterrupted()
//一样都是判断线程是否处于中断(设置的标识的)状态
// 这里有两种状况
// 一种是在非阻塞的情况下,也就是说当 在主线程中去用 interrupt() 方法停止
// 线程的时候,用isInterrupted()会为true,线程会退出循环
// 还有一种是在阻塞的情况下,当调用线程的interrupt()方法时,如使用了sleep,
// 同步锁的wait,socket中的receiver,accept等方法时,使线程处于阻塞状态
// 会使线程先清掉中断标识(isInterrupted() = false 或者
//Thread.interrupted() = false)并且抛出InterruptException异常 ,
// 这个时候就会有两种处理模式 ,一种是直接用break 如下
while (!isInterrupted()){
System.out.println("isInterrupted "+ isInterrupted());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("线程阻塞者,强制退出了");
break;
}
}
}
}
测试方法:
public void testTwo(){
ThreadSafeMethodTwo methodTwo = new ThreadSafeMethodTwo();
methodTwo.start();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
methodTwo.interrupt();
}
结果:
isInterrupted false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.cxl.thread.ThreadSafeMethodTwo.run(ThreadSafeMethodTwo.java:25)
线程阻塞者,强制退出了
显示异常是因为 e.printStackTrace();
还有一种解决思路
public class ThreadSafeMethodThree extends Thread {
@Override
public void run() {
super.run();
// while (!Thread.interrupted()) 和 isInterrupted() 一样都是判断线程是
//否处于中断(设置的标识的)状态
// 这里有两种状况
// 一种是在非阻塞的情况下,也就是说当 在主线程中去用 interrupt() 方法停止
//线程的时候,用isInterrupted()会为true,线程会退出循环
// 还有一种是在阻塞的情况下,当调用线程的interrupt()方法时,如使用了sleep,
//同步锁的wait,socket中的receiver,accept等方法时,使线程处于阻塞状态
// 会使线程先清掉中断标识(isInterrupted() = false 或者
//Thread.interrupted() = false)并且抛出InterruptException异常 ,
// 这个时候就会有两种处理模式 这种处理模式的思路是
//在 catch 中重新设置中断标识 也就是说 Thread.interrupted() = true
// 重新设置为中断状态,
while (!Thread.interrupted()){
System.out.println("线程的当前的中断状态是 false ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("因为 sleep 阻塞 所以抛出了异常,并且我们要给他重
//新 给一个 中断状态 true ");
Thread.currentThread().interrupt();
System.out.println("线程的当前在 Thread.currentThread().interrupt(); 之后的的中断状态是 "+ isInterrupted());
}
}
System.out.println("线程退出了");
}
}
测试方法 和上面一样
结果:
线程的当前的中断状态是 false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.cxl.thread.ThreadSafeMethodThree.run(ThreadSafeMethodThree.java:24)
因为 sleep 阻塞 所以抛出了异常,并且我们要给他重新 给一个 中断状态 true
线程的当前在 Thread.currentThread().interrupt(); 之后的的中断状态是 true
线程退出了
总结一下: 用volatile 设置停止标识,优点是结构简单,但是当 子线程阻塞 时,主线程结束的早,我们是不知道子线程是否结束了的,
下面的两种的思路其实差不多,不过选择上,break 更来的直接一些。