当前位置: 首页 > 科技观察

白话Java线程(二)——让线程优雅地停止

时间:2023-03-17 19:36:13 科技观察

1、前言继续Java多线程前面的内容。之前讲解过Java中多线程的使用。有兴趣的可以先看看《白话说 Java 线程(一)之让线程先跑起来》。但会舞者为徒,能优雅止者为师。接下来让我们看看如何优雅地停止一个线程。2.完全停止线程2.1。safestop涉及的方法当一个线程开始执行一个任务时,如果需要停止,就意味着它会放弃当前的操作。在Java中,停止线程不像返回方法那样简单。安全地停止线程需要一些优雅的技术。如果你想安全地停止一个线程,你需要使用Thread.interrupt()方法。它的本意是停止和终止,但实际上它并不是直接终止一个正在运行的进程,而是在当前线程中,打上一个需要“停止”的标签,是否停止应该由当前线程来决定thread本身,所以这也决定了我们在写Thread或者Runnable代码的时候需要有更高的要求,必须明确我们是如何安全stop的。interrupt()有很多解释,我们来看看它是如何使用的。由于interrupt()只是为我们做了一个简单的当前线程的停止标记,而JDK也为我们提供了获取这个标记值的API。Thread.interrupted():检查当前运行这段代码的线程是否已经停止。this.isInterrupted():检查this指定的当前线程是否已经停止。通过源码可以看出它们的区别。interrupted()是一个静态方法,对当前代码运行的当前线程进行操作,而isInterrupted()方法缺少的操作是指定线程。他们最终都调用了isInterrupted(boolean)方法。让我们再看看这个方法。可以看到,这个方法是native方法,参数表示是否清理Interrupted状态。2.2.看看这些方法的用处。举个例子看看如何区分这两种方法:1表示停止,表示检查线程mt,2表示不停止,因为检查当前运行环境中的线程。那是主线程。这个例子本身就很好,把原意说清楚了。但实际上,interrupt()方法和interrupted()方法都不是线程安全的,也就是说,如果使用interrupt()方法停止了一个线程,立即使用interrupted()方法进行检查,可能返回停止的结果。如前所述,interrupt()和isInterrupted()方法最终将调用本机isInterrupted(boolean)方法。该方法的参数主要是判断是否清理中断状态。下面两个例子会很清楚。首先看一下Thread.interrupted()。可以看到,第一次被标记为true,但是获取之后,中断状态立马被清除,再次获取时,被标记为false。再看isInterrupted(),不会清理状态。2.3.需要安全停止的线程然后继续修改上面的例子。如果想在线程停止后立即停止循环,可以在循环中每次检查当前线程是否已经停止。如果停止了,直接返回,或者在退出前做一些清理。2.4.当线程在睡眠期间停止时会发生什么?当前线程可能正在运行或处于休眠状态。如果一个线程在睡眠状态被中断,会发生什么?从调用sleep的时候应该可以发现,它会抛出一个InterruptedException异常,这个异常是专门用来捕获sleep中被中断的情况的。如果被触发,就会进入catch。并且将中断状态替换为false,所以这里触发sleep的catch时,肯定有后续操作,不要依赖判断线程终止的两种方法来判断。3、如何不安全的停止线程既然推荐安全的方式停止线程,那么不安全的方法应该怎么做呢?其实unsafe方法本身并不推荐,但是研究它为什么不安全也有助于我们理解线程安全。不安全的方式涉及到Thread的几个方法,这些方法都被标记为@Deprecated,也就是说已经被废弃,可能会导致不可预知的问题,所以不推荐使用。这个方法就是stop()方法。顾名思义,它的作用就是停止线程。stop()可以立即停止线程吗?是的,除了一些不可预见的数据问题,stop()方法确实很好用。停止线程可以简单直接地完成。直接停止后,下面的代码根本不会执行,会导致一些清理工作无法完成,stop()会直接释放当前获取的锁,如果是在修改的时候强制停止锁定数据,会导致数据改了一半,造成数据不一致的情况。既然不推荐,就简单举个例子,不再写demo。假设一个人在做生意,卖书,仓库里有需要卖的书。每次的过程就是从仓库里拿出一本书,卖完之后,把钱存入银行账户,然后去取一本继续卖。假设有一天,这个人从仓库里拿了一本书卖了,在去银行存钱的路上,被警察抓了(stop),那么他就无法完成任务去银行存钱。这时,图书库存与银行账户中的金额不匹配,因为一本书出库,账户中没有对应的钱。这种数据不一致就是线程不安全。当然,在实际项目中,这种数据操作是以事务的形式存在的。一旦失败,事务将被回滚以保持数据的一致性。需要注意的一点是,当使用stop()方法时,会触发ThreadDead异常,但不需要被显示捕获,大家只要知道有这样一个概念即可。4.结束语到此为止,子线程的创建和停止已经基本解释清楚了。由于该方法已被弃用,最好不要使用它,建议使用安全的方式停止线程。【本文为专栏作家“张扬”原创稿件,转载请微信公众号联系作者获取授权】点此查看该作者更多好文