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

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

[[210879]]

一、前言

继续接之前 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专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】

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

责任编辑:武晓燕 来源: 51CTO专栏
相关推荐

2017-11-17 15:25:02

Java线程安全

2023-05-12 14:14:00

Java线程中断

2019-08-21 10:15:20

vue组件前端

2018-06-24 09:27:55

线程Tomcat多线程

2023-12-20 10:04:45

线程池Java

2013-06-08 13:07:23

Java线程池调度器

2009-06-11 10:48:53

Java多线程

2011-06-22 13:57:54

Java多线程

2013-07-16 10:57:34

iOS多线程多线程概念多线程入门

2020-03-27 11:41:12

线程 Java中止

2024-02-26 08:28:24

Java线程CPU

2021-09-11 15:26:23

Java多线程线程池

2020-04-29 14:10:44

Java线程池编程语言

2021-12-26 18:22:30

Java线程多线程

2009-06-29 17:49:47

Java多线程

2010-03-18 15:31:13

Java创建线程

2023-11-22 08:37:40

Java线程池

2009-06-29 18:03:15

Java多线程线程的生命周期

2012-02-15 10:34:29

JavaJava Socket

2017-12-18 16:33:55

多线程对象模型
点赞
收藏

51CTO技术栈公众号