interrupt中断与停止线程

线程总共有6中状态,分别是:

New、Runnable、Blocked、Waiting、Timed Wating、Terminated

下面来分析当Runnable的run方法有while循环时,该如何在线程外部停止执行while循环的线程。

先看一段代码

    public static void main(String[] args) throws Exception{
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while (num < Integer.MAX_VALUE){
                    System.out.println(Thread.currentThread().getName() + "正在执行");
                }
            }
        });
        t0.start();

        TimeUnit.MILLISECONDS.sleep(100);

        // 执行t0.interrupt(),但是t0仍会继续运行
        t0.interrupt();
    }

可以看到interrupt()并没有让线程停止运行。

interrput()的作用是将线程的中断状态设置为true,并不是说执行t0.interrupt()之后,t0线程就进入Terminated(终止状态)。

在以下两种情况下,线程会进入Terminated状态:

    1、run方法运行结束。

    2、run方法中抛出异常。

既然interrupt是将线程中断标志设置为true,那就将while循环的判断条件改成判断t0的中断状态,中断状态为false进入while循环,为true退出while循环,当run方法运行结束,线程就变为Terminated状态了。

扫描二维码关注公众号,回复: 10505763 查看本文章

通过  Thread.currentThread().isInterrupted() 可以获取线程的中断状态

    public static void main(String[] args) throws Exception{
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 当前线程的中断状态为true则退出while循环
                while (!Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName() + "正在执行");
                }
            }
        });
        t0.start();

        TimeUnit.MILLISECONDS.sleep(100);

        // 执行t0.interrupt(),t0的中断状态变为true
        t0.interrupt();
    }

运行上面的代码,线程确实停止了。

但是使用isInterrupted()来获取线程的中断状态需要小心,当线程t0抛出InterruptedException的时候,JVM会将t0的中断状态设置为false。

看下面这段代码

    public static void main(String[] args) throws Exception{
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()){
                    try {
                        System.out.println(Thread.currentThread().getName() + "正在执行");

                        // 在线程中加入sleep()这种可以响应中断的代码,并在while循环中把异常捕获
                        TimeUnit.MILLISECONDS.sleep(1);
                    }catch (InterruptedException e){
                        // 发生InterruptedException之后,线程的中断状态会被变成false
                        e.printStackTrace();
                    }
                }
            }
        });
        t0.start();

        TimeUnit.MILLISECONDS.sleep(100);

        t0.interrupt();
    }

上面这段代码就会一直运行下去,因为发生InterruptedException之后,线程的中断状态会变为false。正确的方式应当是这样:在run方法中捕获到到InterruptedException之后,就不会再执行while循环。

    public static void main(String[] args) throws Exception{
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (!Thread.currentThread().isInterrupted()){
                        System.out.println(Thread.currentThread().getName() + "正在执行");
                        // 在线程中加入sleep()这种可以响应中断的代码
                        TimeUnit.MILLISECONDS.sleep(1);
                    }
                // 将捕获异常的代码放到while循环之外,捕获异常之后不再有循环代码
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        });
        t0.start();

        TimeUnit.MILLISECONDS.sleep(100);

        t0.interrupt();
    }

上面的代码不会一直运行,即线程t0会终止。

当我们在run方法中catch到InterruptedException一定要想到线程的中断状态被JVM自动设置为false了。

还有一种方式是使用volatile修饰一个boolean变量来停止while,但这种方式也是有bug的,不能做到立刻停止线程。

    // 停止生产标识
    public static volatile boolean stopProduce = false;

    public static void main(String[] args) throws Exception{
        // 资源队列,用于公共存储资源
        LinkedBlockingQueue lbq = new LinkedBlockingQueue(3);

        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                try {
                    while (i<Integer.MAX_VALUE && !stopProduce){
                        System.out.println("生产 "+i);
                        /**
                         * 当LinkedBlockingQueue队列满了,put方法会将线程设置为WAIT状态
                         * 若没有消费者消费LinkedBlockingQueue中的资源,线程会一直在这里等待,没法运行while判断
                         */
                        lbq.put(i);
                        i++;
                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        });
        t0.start();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (new Random().nextInt(100)>5){
                        // 消费资源
                        System.out.println("消费 "+lbq.take());
                    }
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        });
        t1.start();

        TimeUnit.MILLISECONDS.sleep(10);

        // 设置停止生产标识为true,但是t0立刻没有停止,还处于WAIT状态
        stopProduce =true;

        // 消费一个LinkedBlockingQueue的资源后,t0从WAIT状态变为RUNNABLE,执行while判断后退出循环,线程结束
        //lbq.take();
    }
发布了51 篇原创文章 · 获赞 14 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u010606397/article/details/103891954