线程的终止-面试题stop和interrupt方法区别?

了解了线程的生命周期之后,我们知道,一个线程执行完run方法,或者发生异常之后都会自动终止。
但是,这里要说的是在一个线程A中去终止另一个线程B的情况。

在Thread类里面有2个方法可以达到此目的。stop()interrupt()

  • stop()方法:用stop() 方法会真的杀死线程,不给线程喘息的机会,如果线程持有 ReentrantLock 锁,被 stop() 的线程并不会自动调用 ReentrantLock 的 unlock() 去释放锁,那其他线程就再也没机会获得 ReentrantLock 锁,这实在是太危险了。所以该方法就不建议使用了,类似的方法还有 suspend() 和 resume() 方法,这两个方法同样也都不建议使用了

比如说下方的i++的例子,我们是通过Lock的方式实现原子操作的。

public void addI() {
    lock.lock();
    try {
        i++;
    } finally {
        lock.unlock();
    }
}

这样会出现什么问题呢?当线程A执行时,线程B终止了A的操作,如果A恰好持有共享锁lock,那么它并不会执行unlock操作,导致想获得锁的线程B永远也不能获得锁(不过有的同学可能说给lock加个超时,这个就另说了)

ps:如果是synchronized实现的原子操作,是会主动释放锁的。synchronized原理参考之前写的这篇:大彻大悟synchronized原理,锁的升级

  • interrupt():从线程的生命周期了解到,一个线程要终止,必须要从runnable状态到terminated。而线程可能处于休眠状态,interrupt方法恰好提供了此功能,它可以将休眠状态的线程转换到runnable状态,并置标志位为true(此时Thread.currentThread().isInterrupted()==true)。

那么线程B调用了线程A的interrupt方法,就能终止线程A吗?
有2种情况:

  • 线程处于waiting或者time_waiting状态:当线程 A 处于 WAITING、TIMED_WAITING 状态时,如果线程B调用线程 A 的 interrupt() 方法,会使线程 A 返回到 RUNNABLE 状态,同时线程 A 的代码会触发 InterruptedException 异常。转换到 WAITING、TIMED_WAITING 状态的触发条件,都是调用了类似 wait()、join()、sleep() 这样的方法,我们看这些方法的签名,发现都会 throws InterruptedException 这个异常。这个异常的触发条件就是:其他线程调用了该线程的 interrupt() 方法。
    有一点需要注意的是,抛出异常之后JVM 的异常处理会清除线程的中断状态,我们需要用Thread.currentThread().interrupt() 重新设置了线程的中断状态
  • 非waiting状态:这种状态下,线程B调用线程A的interrupt方法,线程A通过Thread.currentThread().isInterrupted()能判断到自己已被中断,
    然后自己结束。

所以结合上面2中情况,我们有了下面的例子:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class StopInterruptTest {
    private int i = 0;
    Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        StopInterruptTest test = new StopInterruptTest();
        Thread t1 = new Thread(() -> {
            // 中断标志判断
            while (!Thread.currentThread().isInterrupted()) {
                test.addI();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 重置标志位
                    Thread.currentThread().isInterrupted();
                }
            }
        });

        t1.start();
        // main线程打断线程t1
        t1.interrupt();
    }

    public void addI() {
        lock.lock();
        try {
            i++;
        } finally {
            lock.unlock();
        }
    }
}

但是这样可能有其他问题,就是:如果在t1线程里面,执行的是其他的业务逻辑,其他的业务逻辑并没有正确的处理中断标志。这样就可能出现其他诡异问题。

我们要做的就是,设置自己的标志位,封装自己的终止方法等。比如下面的interruptFlag。

public class StopInterruptTest {
    volatile boolean interruptFlag = false;

    public static void main(String[] args) {
        StopInterruptTest test = new StopInterruptTest();
        test.testInterrupt();
    }

    public void testInterrupt() {
        Thread t1 = new Thread(() -> {
            // 中断标志判断
            while (!interruptFlag) {
                // 其它业务
                // test.addI();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 重置标志位
                    Thread.currentThread().isInterrupted();
                }
            }
        });
        t1.start();
        // main线程打断线程t1
        myInterrupt(t1);
    }

    public synchronized void myInterrupt(Thread t) {
        interruptFlag = true;
        t.interrupt();
    }

}

多线程连载:
Java内存模型-volatile的应用(实例讲解)
synchronized的三种应用方式(实例讲解)
可重入锁-synchronized是可重入锁吗?
大彻大悟synchronized原理,锁的升级
一文弄懂Java的线程池
公平锁和非公平锁-ReentrantLock是如何实现公平、非公平的
一图全面了解Java线程的生命周期
守护线程和用户线程的真正区别(实例讲解)
搞定等待通知机制-wait/notify/notifyall的2个经典面试题(实例详解)

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值