您的位置 首页 java

白话说 Java 线程(二)之让线程优雅的停下来

一、前言

继续接之前 Java 多线程 的内容,之前讲解了 Java 下多 线程 的使用,有兴趣的可以先看看《白话说 Java 线程(一)之让线程先跑起来》。但是能舞的起来是徒弟,能停的优雅才是师傅。

接下来让我们看看,如何优雅的停止一个线程。

二、全的停止线程

2.1、安全停止涉及到的方法

当开启一起线程去执行任务之后,如果需要被停止,意味着它将放弃当前正在进行的操作。而在 Java 中,停止一个线程并不像 return 一个方法一样干脆利落,想要安全的停止线程,需要一些跟优雅的技巧。

如果想要安全的停止一个线程,需要借助 Thread.interrupt() 方法,它的本意是停止、终止的意思,但是实际上,它并不会直接终止掉一个正在运行的进程,而是在当前线程中,打一个需要” 被停止 “的标签,而是否停止应该由当前线程自己决定,所以这也决定了我们需要在编写 Thread 或者 Runnable 代码的时候,有更高的要求,要明确自己如何被安全的停止。

interrupt() 的解释确实挺多,接下来看看它是如何使用的。

既然 interrupt() 只是为我们对当前线程做了一个简单的停止标记,而 JDK 同时也为我们提供了获取这个标记值的 API。

  • Thread.interrupted() :检查当前运行这段代码的线程,是否已经被停止。

  • this.isInterrupted() :检查当前 this 指定的线程,是否已经被停止。

通过源码可以看到它们的区别, interrupted() 是一个 static 的方法,并且操作的是当前代码运行的当前线程,而 isInterrupted() 方法缺失操作的是指定线程。它们最终都会调用 isInterrupted( boolean ) 的方法。

再来看看这个方法。

可以看到,这个方法是一个 native 的方法,参数表示是否需要清理这个 Interrupted 的状态。

2.2、具体看看这些方法的用处

举个例子看看如何区分这两个方法:

1 为停止,表示检查的是 mt 这个线程,而 2 为没停止,是因为检查的是当前运行环境的线程,即为主线程。

这个例子本身没问题,也讲清楚了原本的意思。但是实际上, interrupt() 方法和 interrupted() 方法并不是 线程安全 的,也就是说,如果使用 interrupt() 方法停止了一个线程,立即使用 interrupted() 方法去检查,可能会回去到还没有被停止的结果。

前面介绍到, interrupt() isInterrupted() 方法最终都会调用一个 native 的 isInterrupted(boolean) 的方法,这个方法的参数主要是为了确定是否需要清理 interrupted 的状态。

下面举两个例子就清楚了。

先看看 Thread.interrupted()

可以看到,第一次标记为 true,但是获取完之后,立即就被清理掉了 interrupt 状态,再去获取,就标记为 false。再看看 isInterrupted() ,它是不会清理掉状态的。

2.3、需要安全停止的线程

那么继续改造一下上面的例子,如果想在线程被停止之后,立即停止掉循环,就可以在循环中,每次调用都检查一下当前线程是否已经被停止了。如果被停止了,直接 return 出去,或者做一些清理工作再退出。

2.4、在sleep的时候,停止线程会发生什么

当前线程有可能在运行状态,也可能在 sleep 的状态,如果在 sleep 的状态下,interrupt 一个线程,会发生什么?

从调用 sleep 的时候就应该发现,它是会抛出一个 InterruptedException 异常的,这里就是专门为了捕捉在 sleep 的时候,被 interrupt 的情况。如果触发,将进入 catch。并且置换 interrupt 状态为 false ,所以这里触发了到 sleep 的 catch 的时候,一定要有后续的操作,不要再依赖那两个判断线程终止的方法来判断了。

三、如何非安全的停止线程

既然推荐用安全的方式来停止线程,那么不安全的方式又需要怎么做呢?不安全的方式其实本身也不推荐使用,但是研究一下为什么不安全也有利于我们理解线程安全。

不安全的方式,就涉及到 Thread 的几个已经被标记为 @Deprecated 的方法,就是说,已经被废弃了,可能引发不可预料的问题,不推荐使用。

这个方法就是 stop() 方法,和名称一样,它的作用就是停止线程。

stop() 能不能做到立即停止线程?

能,除了它的一些不可能预料的数据问题之外, stop() 方法真的非常的好用,停止线程可以做到简单直接,直接被停止之后,后面的代码根本不会得到执行,这样会导致一些清理工作没法完成,并且 stop() 会直接释放当前获取到的锁,而如果当前正好在修改加锁的数据的时候,被强制停止了,就会导致数据被改了一半,导致数据不一致的情况。

既然不推荐使用,那就简单举个例子,不再写 demo 了。

假设一个人做生意,卖书,库房里存了需要卖的书,每次的流程是从库房里拿出来一本书,卖出去之后,把钱存入银行账户,再去取一本书继续卖。假设有一天,这个人从库房取了一本书卖出去了,正在去银行存钱的路上,被警察逮捕了(stop),接下来他就没办法完成去银行存钱的任务了。这个时候对书的库存和银行账户上的金额,就对不上了,因为出库了一本书,缺没有相应的金钱入账。这样的一个数据不一致,就是线程不安全。当然,实际项目中,这种数据操作都是以事务的形式存在的,一旦失败就会回滚事务,让数据保持一致。

有一点需要注意一下, stop() 方法的时候,会触发 ThreadDead 异常,但是它不需要被显示 Catch 住,大家只需要知道有这么个概念就可以了。

四、结语

到现在基本上就讲解清楚了子线程的创建和停止。既然已经被废弃的方法,最好还是不要去使用它,建议使用安全的方式去停止线程。

我准备了一些我整理的学习资料,包含:Android反编译、算法、设计模式、Java并发编程、Web项目源码。如果有兴趣可以私信我。

文章来源:智云一二三科技

文章标题:白话说 Java 线程(二)之让线程优雅的停下来

文章地址:https://www.zhihuclub.com/182659.shtml

关于作者: 智云科技

热门文章

网站地图