线程的优雅关闭
线程的优雅关闭
前言
我们都知道线程是一段运行中的代码,既然在运行中,我们怎么将它停下来。
能否强制让它停下来?强制停下来,是否会存在问题?
有什么好的办法,可以优雅的让其停下来?
如何优雅的关闭一个线程
在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() 读取中断标志