关于线程的interrupt与sleep问题

关于线程的interrupt与sleep问题

关于这个问题我找到了很多帖子,他们都说:

先sleep,后才能interrupt。 sleep是阻塞,线程会暂时停在这里。interrupt 是打断。只有阻塞的线程被打断了,才会报这个异常。

然后,我作为初学者经过实际的代码测试的时候就很蒙。我们看组代码:

情况一:自定义线程中写interrupt与sleep

public class SubThread4 extends Thread {
    @Override
    public void run() {
        System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("线程的id:" + Thread.currentThread().getId());
        System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("当前状态:" + this.isInterrupted());
        try {
            System.out.println(this.isInterrupted());
            System.out.println("打断后:"+this.isInterrupted());
            Thread.currentThread().interrupt();
            sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            //打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
            //事实证明先打断后sleep才会抛出异常。。。
            //在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
            //            System.out.println("调用过sleep后:"+this.isInterrupted());
            System.out.println("进入异常");
            System.out.println(this.isInterrupted());
            return;
        }

        System.out.println("当前状态:" + this.isInterrupted());
        System.out.println("这个线程执行完了!");
    }

}

在这里插入图片描述

public class SubThread4 extends Thread {
    @Override
    public void run() {
        System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("线程的id:" + Thread.currentThread().getId());
        System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("当前状态:" + this.isInterrupted());
        try {
            System.out.println(this.isInterrupted());
            System.out.println("打断后:"+this.isInterrupted());
            sleep(3000);
            Thread.currentThread().interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
            //打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
            //事实证明先打断后sleep才会抛出异常。。。
            //在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
            //            System.out.println("调用过sleep后:"+this.isInterrupted());
            System.out.println("进入异常");
            System.out.println(this.isInterrupted());
            return;
        }

        System.out.println("当前状态:" + this.isInterrupted());
        System.out.println("这个线程执行完了!");
    }

}

在这里插入图片描述
      这两组代码只有sleep(3000);和Thread.currentThread().interrupt();这两行代码调换位置,这一点不同,其余部分完全一样。
      但结果好像和网上说的“先sleep,后才能interrupt,抛异常”顺序不一样啊!实际上它们是一样的,网上的分析都光分析了内部原理内部原理确实是先sleep后interrupt就会抛出异常,但他们都没说实际编码是个啥样,我也是想了半天才晕过来,在实际编程中我们的sleep调用的是Thread为我们封装好的sleep方法,也就是说sleep已经封装好了,你不可能在sleep的每一毫秒过程中都去检测或者调用interrupt方法。编码中的sleep是一个整体,它需要完全睡完,之后才能执行第二行的Thread.currentThread().interrupt();,而这个时候它都已经睡完了当然不会发生sleep中打断的情况,所以编码的这个时候 先sleep(3000);后Thread.currentThread().interrupt();是没有任何异常的。
      相反的,先使用Thread.currentThread().interrupt();将线程的中断标志调为true,这个时候我们再来写sleep(3000);它会发现中断标记已经为true了,它没法再阻塞这个线程了,就会出现异常并捕获异常。注意:这个时候它会捕获异常是不假,但它并不会返回,我们需要手动在catch中加一个return;返回并结束线程,这个也是线程的特性,它只会捕获但并不能将异常抛出后结束线程。

       所以以后要用这种方法结束线程,编码时是先写interrupt后写sleep这样才能抛异常。



情况二:主线程中写

这种就是大概这样你写好了自定义线程代码(就把Thread.currentThread().interrupt();删掉了interrupt();放在主线程里面):

public class SubThread4 extends Thread {
    @Override
    public void run() {
        System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("线程的id:" + Thread.currentThread().getId());
        System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("当前状态:" + this.isInterrupted());
        try {
            System.out.println(this.isInterrupted());
            System.out.println("打断后:"+this.isInterrupted());
//            Thread.currentThread().interrupt();
            sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            //打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
            //事实证明先打断后sleep才会抛出异常。。。
            //在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
            //            System.out.println("调用过sleep后:"+this.isInterrupted());
            System.out.println("进入异常");
            System.out.println(this.isInterrupted());
            return;
        }

        System.out.println("当前状态:" + this.isInterrupted());
        System.out.println("这个线程执行完了!");
    }

}

主方法:

public class Test {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+" , id = "+Thread.currentThread().getId());
        SubThread4 t4 = new SubThread4();
        System.out.println("当前时间:"+System.currentTimeMillis());
        t4.start();
        t4.interrupt();
        System.out.println("睡眠之后的时间:"+System.currentTimeMillis());
    }
}

结果:
在这里插入图片描述
      你可能觉得interrupt在线程start之后但还是抛出异常信息了,是不是编码按照先sleep后interrupt的顺序执行了呢? 错! 代码执行顺序依旧是先interrupt后sleep所以才抛得异常信息。 因为主线程里面开别的线程的方式虽然是直接调用start方法,但start方法调用后线程并没有直接执行,而是将该线程交给了线程调度器(schedule) 线程真正什么时候运行是不确定的。(但是一般都是main线程执行完才开始执行别的线程的 ,这里的一般是指JDK1.8在IDEA2018上的执行顺序,没错这就是我电脑的环境。。。XD)

      所以综上所述,这种方式,还是执行了先interrupt方法后执行了sleep方法才抛出的异常

再换一种情况

代码:

public class SubThread4 extends Thread {
    @Override
    public void run() {
        System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("线程的id:" + Thread.currentThread().getId());
        System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
        System.out.println("当前状态:" + this.isInterrupted());
//        try {
//            System.out.println(this.isInterrupted());
//            System.out.println("打断后:"+this.isInterrupted());
            Thread.currentThread().interrupt();
//            sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//            //打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
//            //事实证明先打断后sleep才会抛出异常。。。
//            //在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
//            //            System.out.println("调用过sleep后:"+this.isInterrupted());
            System.out.println("进入异常");
            System.out.println(this.isInterrupted());
            return;
//        }

        System.out.println("当前状态:" + this.isInterrupted());
        System.out.println("这个线程执行完了!");
    }

}

主线程:

public class Test {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+" , id = "+Thread.currentThread().getId());
        SubThread4 t4 = new SubThread4();
        System.out.println("当前时间:"+System.currentTimeMillis());
        t4.start();
//        try {
//            sleep(4000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        Thread.currentThread().interrupt();
        try {
            sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            return;
        }

//        t4.interrupt();
        System.out.println("睡眠之后的时间:"+System.currentTimeMillis());
    }
}

结果:
在这里插入图片描述
我们可以很明显的看到主线程被打断了
在这里插入图片描述
主线程中这一句代码没有执行。
      但子线程执行了,你可能会在一开始觉得子线程永远在主线程运行完才执行,主线程要停了子线程就不执行了。

      但子线程依旧执行了,虽然主线程被打断睡眠抛出了异常还调用了return将主线程结束,线程调度器在主线程结束前执行了子线程。说明线程执行顺序不确定,且在编码中先写interrupt后写sleep才能抛出异常实现想要的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值