Java如何正确停止线程(一)

正常情况下,一个线程在启动后执行相应的业务逻辑,直到运行结束;线程停止。但是在有些时候,我们需要在线程运行时人为的停止线程,比如用户关闭程序;自然要快速的停止相应线程;然后释放资源。那么我们该如何科学的停止线程?

1.一般线程在什么情况下会停止?

一般来说,线程正常运行时只有在两种情跨过下会停止:

  • run()中的代码执行完毕,线程停止
  • 线程运行过程中抛出异常,线程停止

2.原则

想要科学的停止线程,必须掌握一个原则那就是通知中断而不是强制停止;什么意思呢?首先我们要知道在Java中想要停止一个线程;不能强制停止,如果强行停止线程可能会引起数据错乱。而是应该给线程一个中断信号;告诉它:“兄弟,你尽快停止吧”;线程在收到这个信号后,就会进行一些收尾工作;比如释放资源、关闭流和网络连接、临时数据的持久化等。但至于线程在什么时候停止?停不停止?都是由线程自身去决定的。换句话说,作为想停止线程的一方;不能粗暴的直接终止该线程。而是温柔的给它一个通知,告诉它该停止了。Java提供的这种停止线程的合作机制就是interrupt中断机制。

那么Java为什么要这么设计呢?其实也不难理解,我们在平时下班关机的时候,也并不是主机马上就停止运行;而是逐个关闭运行中的应用程序。释放资源然后主机才停止运行。况且,在一个项目中;我们作为想要停止线程的一方,可能并不了解被停止的线程的业务逻辑是怎样的;并且大多数时候我们停止一个线程都是想让它做好收尾工作再停止,并不想出现数据错乱;所以Java在设计的时候,停止线程的决定权在于线程自身。

综上所述,停止线程的关键在于如何正确的给线程传递interrupt中断信号,以及被停止的线程收到中断信号后该如何配合的。

2.实践

接下来,本文讲通过代码演示来说明在项目实践中该如何正确的停止线程;本文将分三种情况进行说明,因为三种情景的停止线程策略有所不同。这三种情况分别为:

  • 无阻塞的情况下停止线程
  • 阻塞的情况下停止线程
  • 每次迭代后都阻塞的情况下停止线程

2.1 无阻塞的情况下停止线程

先来看第一种情况:

public class StopThreadNotBlock implements Runnable {

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        int i = 1 ;
        while (i <= 1000000) {
            System.out.println((i++) + ":现在,我们是是同志了!");
        }
        long end = System.currentTimeMillis();
        System.out.println("finish! "+ (end - start) +"ms");
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new StopThreadNotBlock());
        thread.start();
    }

}

在上面的代码中,我启动了一个线程;这个线程的任务是打印100万行《三体》名言“现在,我们是同志了!”在前面加上了行号。并统计了执行的时长(毫秒),先看下执行结果:

共打印出100万行“现在,我们是同志了!”,执行时长 4286ms

public class StopThreadNotBlock implements Runnable {

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        int i = 1 ;
        while (i <= 1000000) {
            System.out.println((i++) + ":现在,我们是是同志了!");
        }
        long end = System.currentTimeMillis();
        System.out.println("finish! "+ (end - start) +"ms");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadNotBlock());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt(); // 发送中断信号
    }

}

任务执行完成共需要4000ms左右,现在;我在线程启动1000ms后从主线程给这个线程发送一个interrupt中断信号,此时任务肯定还没有实行完毕,我们看看会发生什么?

还是和之前一样打印出了100万条记录,咦?我们不是已经发送中断信号了吗?怎么线程还是执行到了最后?其实这是正常的,在上文原则部分已经说了:作为想终止线程的一方,我们能做的就是给它一个interrupt中断信号;而线程停不停止?什么时候停止是由线程内部决定的。

想到这里,我们可以发现:在被终止的线程的run()方法中没有做额外的处理中断信号的操作。所以即使我们发送了中断信号,线程内部没有处理的话;线程还是无法停止。那我们现在修改线程中的run()方法:

public class StopThreadNotBlock implements Runnable {

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        int i = 1 ;
        // <A> 判断interrupt中断信号
        while (!Thread.currentThread().isInterrupted() && i <= 1000000) {
            System.out.println((i++) + ":现在,我们是是同志了!");
        }
        long end = System.currentTimeMillis();
        System.out.println("finish! "+ (end - start) +"ms");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadNotBlock());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt(); // 发送中断信号
    }

}

在上面的代码<A>Thread.currentThread()获取到的就是当前线程对象,而Thread.currentThread().isInterrupted()获取的是当前线程是否收到了中断信号,true表示收到了中断信号应该结束线程了;false表示没有收到中断信号,可以继续执行。我们来看看执行结果:


可以发现线程打印到20万行左右就停止了,并且运行时长为1005ms,而我们恰恰是在线程启动1000ms后发出的中断信号;
如果我是在线程启动2000ms后从主线程给这个线程发送一个interrupt中断信号我们可以推测它大概会打印四五十万行左右,并且运行时长应该为2000ms左右

可见,我们的中断信号被线程接收并处理并线程在中途结束了;中断信号有效。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值