> 文档中心 > 线程的优雅关闭

线程的优雅关闭


线程的优雅关闭

前言

我们都知道线程是一段运行中的代码,既然在运行中,我们怎么将它停下来。
能否强制让它停下来?强制停下来,是否会存在问题?
有什么好的办法,可以优雅的让其停下来?

如何优雅的关闭一个线程

在JAVA中,一个线程一旦运行起来,就不要去强行打断它,合理的关闭是让它运行完,然后干净的释放资源,最后退出。
如果一旦强制打断,那么线程中使用的资源如io等资源不会被正确释放,这么做是存在一定问题的。

关于线程退出的几种方式

首先JDK中给我们提供了销毁和停止线程的方法
可以看到stop以及destroy方法,但都被官方弃用了。
在这里插入图片描述
在这里插入图片描述
我们可以看到jdk中有这么一段描述
在这里插入图片描述
这种方法本身就是不安全的。使用thread停止一个线程。stop会导致它解锁它锁定的所有监视器(这是未检查ThreadDeath异常向上传播堆栈的自然结果)。如果这些监控器以前保护的任何对象处于不一致的状态,则被损坏的对象对其他线程来说是可见的,这可能会导致任意行为。stop的许多用法应该被代码所取代,这些代码只需修改一些变量来指示目标线程应该停止运行。目标线程应该定期检查这个变量,如果变量表明它将停止运行,则应该有序地从它的run方法返回。如果目标线程等待很长时间(例如,在一个条件变量上),那么应该使用interrupt方法来中断等待。

JDK中建议可以使用一个变量和interrupt来中断一个线程

这里我们使用 interrupt 来打断线程

//atomicBoolean  这里使用可见性的布尔变量打断,对其线程可见,变量可见性是volatile修饰的,线程获取变量会从主内存去获取,而不是从自己本地变量获取,如果此变量不是volatile修饰,你会发现线程可能没有停止下来。 AtomicBoolean atomicBoolean  = new AtomicBoolean(); Thread thread = new Thread(() -> {     int i = 0;     while (true) {  System.out.println(i++);  try {      TimeUnit.SECONDS.sleep(1);  } catch (InterruptedException e) {      e.printStackTrace(); }  if (atomicBoolean.get()){      return;  }     } }); thread.start(); try {     TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {     e.printStackTrace(); } System.out.println(" 终端标志 : " +  thread.isInterrupted() ); TimeUnit.SECONDS.sleep(5); System.out.println("flag 之后 终端标志 : " +  thread.isInterrupted() ); TimeUnit.SECONDS.sleep(5); atomicBoolean.set(true); thread.interrupt(); //TimeUnit.SECONDS.sleep(2); System.out.println("interrupt 之后 终端标志 : " +  thread.isInterrupted() ); TimeUnit.SECONDS.sleep(5);

我们来看一下结果

在这里插入图片描述
可以看到我们线程的确被中断了,睡眠的时候被中断,但是线程状态是false,但是我们的线程是的确是中断了,但是为什么isInterrupted中断返回false呢?

这是因为,isInterrupted 只有线程已经被中断后才会返回tue,其他状态被中断返回都是false(中断状态会被清除,收到InterruptedException)

上面是我们线程在睡眠过程中被中断的,不是正常运行中被中断,所以返回false

可以看到jdk中是这么描述的
在这里插入图片描述

我们将上面睡眠状态代码删除后,重新执行会发现线程会正常停止
在这里插入图片描述

在JAVA中
一个JVM中开多个线程时,这些线程被分为2中,一种是守候线程,另一种是非守护线程,默认都是非守护线程。
在JAVA中有一个规定,当所有非守护线程退出后,整个JVM进程就回退出。

我们可以将线程设置为守护线程,这样当main线程退出时,线程也能退出了。

  Thread thread = new Thread(() -> {     int i = 0;     while (true) {  System.out.println(i++);  try {      Thread.sleep(1_000);  } catch (InterruptedException e) {      e.printStackTrace();  }     } }); thread.setDaemon(true); thread.start(); try {     TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {     e.printStackTrace(); }

可以看到正确退出

在这里插入图片描述

其实 synchronized 也可以将线程停止下来,这里就牵扯到线程变量可见性问题了,synchronized属于重量级,而volatile属于轻量级

   static boolean flag = true;     static  final Object lock = new Object();    public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() {     @Override     public void run() {  while (flag) {      synchronized (lock) {   System.out.println(" ---- ");      }  }     } }).start(); Thread.sleep(1000); synchronized (lock){     flag = false; } System.out.println(" main end ");    }

这样也可以退出线程,但synchronized来说要获取锁,volatile不需要获取锁,仅仅是将变量可见,对比synchronized来说比较轻量。

最后

关于isInterrupted() interrupt() 方法

interrupt() 相当于给线程发送了一个发送一个中断信号
isInterrupted() 读取中断标志