|
|
|
|
移动端
创建专栏

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

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

作者:张旸|2017-11-22 15:11

技术沙龙 | 6月30日与多位专家探讨技术高速发展下如何应对运维新挑战!


一、前言

继续接之前 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 住,大家只需要知道有这么个概念就可以了。

四、结语

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

【本文为51CTO专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】

戳这里,看该作者更多好文

【编辑推荐】

  1. 外媒速递:一场开发者支持率的史诗对决——Node.js对Java
  2. jvm系列(九):如何优化Java GC「译」
  3. 深入理解Java帝国之泛型详解
  4. Java虚拟机是如何执行线程同步的
  5. 白话说Java线程(一)之让线程先跑起来
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

热门职位+更多