如何正确关闭多线程

java创建多线程是基础中的基础,继承Thread类、实现Runnable接口和使用Callable和Future创建线程,或者使用线程池创建,这里就不阐述了,那么我们直接进入话题如何正确优雅的关系运行的线程呢?

在以往项目中,我经常看到一些人写的线程如下

public class ThreadDemo extends Thread {
 // 建立标记位 控制线程执行
 private volatile boolean flag;
 public boolean isFlag() {
 return flag;
 }
 public void setFlag(boolean flag) {
 this.flag = flag;
 }
 //无参构造方法传入,或者给予一个默认值
 ThreadDemo(boolean flag){
 this.flag = flag;
 }
 @Override
 public void run() {
 while (flag) {
 System.out.println("标记执行线程 running...");
 }
 }
}
//测试类
class runDemo{
 //isInterrupted() true如果这个线程已被中断; false否则。
 //interrupt() 				中断这个线程
 //interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
 public static void main(String[] args) throws InterruptedException {
 ThreadDemo threadDemo = new ThreadDemo(true);
 threadDemo.start();
 threadDemo.setFlag(false);
 //查看这个方法是否被中断
 System.out.println(threadDemo.isInterrupted()); 
 //不出意外绝b false
 }
}

这说明我jdk自带的方法判断竟然返回了flase,但是上这个方法也确实得到了中断,所以这种方法不推荐,真确的写法应该是这个样子的,去除标记位

public class ThreadDemo extends Thread {
 ThreadDemo(){ }
 @Override
 public void run() {
 while (!isInterrupted()) {//调用原生方法
 System.out.println("标记执行线程 running...");
 }
 }
}
//测试类
class runDemo{
 //isInterrupted() true如果这个线程已被中断; false否则。
 //interrupt() 				中断这个线程
 //interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
 public static void main(String[] args) throws InterruptedException {
 ThreadDemo threadDemo = new ThreadDemo();
 threadDemo.start();
 TimeUnit.SECONDS.sleep(2);
 //调用中断方法
 threadDemo.interrupt();
 //查看这个方法是否被中断 结果返回true,中断成功
 System.out.println(threadDemo.isInterrupted());
 }
}

那么只是简单的这个样子是否认为就完整了呢?答案是不完整的,并且还有缺陷。大家如果仔细观察Thread api 会发现查用的join,sleep等等操作的线程api都会抛出异常java.lang.InterruptedException。下面我们就模拟一下,就是简单的 加了个睡眠,在睡眠过程中断

public class ThreadDemo extends Thread {
 ThreadDemo(){
 }

 @Override
 public void run() {
 while (!isInterrupted()) {
 try {
 System.out.println("标记执行线程 running...");
 //模拟睡眠中,我就执行中断
 TimeUnit.SECONDS.sleep(200);
 } catch (InterruptedException e) {
 System.out.println("打印标记状态isInterrupted="+isInterrupted());
 //打印标记状态isInterrupted=false
 e.printStackTrace();
 }
 }
 }
}
//测试类
class runDemo{
 //isInterrupted() true如果这个线程已被中断; false否则。
 //interrupt() 中断这个线程
 //interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
 public static void main(String[] args) throws InterruptedException {
 ThreadDemo threadDemo = new ThreadDemo();
 threadDemo.start();
 TimeUnit.SECONDS.sleep(2);
 //调用中断方法
 threadDemo.interrupt();
 //查看这个方法是否被中断
 System.out.println(threadDemo.isInterrupted());
 }
}

通过执行会发现抛出了异常 java.lang.InterruptedException: sleep interrupted。所以这个用例还是有问题,当外层调用了中断方法,里面的线程还在运行就会抛出异常并把标记位isInterrupted还原成flase,所以我们需要再次调用中断,合理的让他再次重置标记。改造最后步骤。

public class ThreadDemo extends Thread {
 ThreadDemo(){}
 @Override
 public void run() {
 while (!isInterrupted()) {
 try {
 //模拟睡眠中,我就执行中断
 System.out.println("标记执行线程 running...");
 TimeUnit.SECONDS.sleep(200);
 } catch (InterruptedException e) {
 System.out.println("打印标记状态isInterrupted="+isInterrupted());
 interrupt();
 System.out.println("打印标记状态isInterrupted="+isInterrupted());
 if (!isInterrupted()) {
 e.printStackTrace();
 }
 }
 }
 }
}
//测试类
class runDemo{
 //isInterrupted() true如果这个线程已被中断; false否则。
 //interrupt() 中断这个线程
 //interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
 public static void main(String[] args) throws InterruptedException {
 ThreadDemo threadDemo = new ThreadDemo();
 threadDemo.start();
 TimeUnit.SECONDS.sleep(2);
 //调用中断方法
 threadDemo.interrupt();
 //查看这个方法是否被中断
 System.out.println(threadDemo.isInterrupted());
//运行结果
// 标记执行线程 running...
// true 可以看到第一次调用interrupt() 然后咱们输出了一下isInterrupted 是true 但是在下面打印的时候又被重置了false
// 打印标记状态isInterrupted=false
// 打印标记状态isInterrupted=true
 }
}

以上就是如何正确的使用线程并如何优雅的关闭线程,所以下次大家记得一定要用原生的方法,不要用布尔类型的值去做set和get操作。

原文地址:https://www.cnblogs.com/liclBlog/p/15349476.html