正文共: 2561字 8图
预计阅读时间: 7分钟
一、前言
继续接之前 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反编译、算法、设计模式、Web项目源码。
推荐阅读:
目前5000+人已关注加入我们
听说喜欢留言的人,运气都不会太差~
点击『阅读原文』查看更多精彩内容