java中如何终止线程的执行

线程被创建后,当run()方法执行完毕,线程便会正常结束和销毁。但是,在有些情况下,run() 方法是永远不会执行完的,比如在run()方法中进行while()轮询任务时,这种情况下就需要我们通过代码手动的退出线程。 这里我们介绍以下几种方法:

1. 使用退出标识

添加一个boolean类型的变量isRun作为线程的退出标识,当isRun为false时,结束循环以中断线程。

public class CloseThread {

    public static class CreateThread extends Thread {
        //定义退出标志,true一直执行,false退出循环
        public volatile boolean isRun = true;

        @Override
        public void run() {
            System.out.println("创建线程完成");
            while (isRun) {
                System.out.println("工作中..., thread name =" + Thread.currentThread().getName());
                SleepUtils.second(1);//线程停止1秒
            }
            System.out.println("创建执行完毕");
        }

        public void interrupt() {
            System.out.println("中断标识设置为false");
            this.isRun = false;
        }
    }

    public static void main(String[] args) {
        //实例化一个线程并 start()
        Thread thread = new CreateThread();
        thread.start();
        //线程停止5秒
        SleepUtils.second(5);

        System.out.println("尝试中断线程");
        thread.interrupt();
    }
}
复制代码

执行代码,打印以下结果:

创建线程完成
工作中..., thread name =Thread-0
工作中..., thread name =Thread-0
工作中..., thread name =Thread-0
工作中..., thread name =Thread-0
工作中..., thread name =Thread-0
开始尝试中断线程
中断标识设置为false
线程执行完成
复制代码

在main()中调用interrupt(),设置线程的中断标识为false,最终成功结束了循环中断了线程的执行。

2. 调用线程本身的interrupt()方法

interrupt()方法是线程本身的一种中断标识,通过调用这个方法可以将线程的isInterrupted()设置为true。

public class CloseThread2 {

    public static class CreateThread2 extends Thread {

        @Override
        public void run() {
            while (!currentThread().isInterrupted()) {
                System.out.println("工作中...");
            }
            System.out.println("线程执行完成");
        }
    }

    public static void main(String[] args) {
        //实例化一个线程并start()
        CreateThread2 thread = new CreateThread2();
        thread.start();
        //当前线程停止1s
        SleepUtils.second(1);

        //调用线程的interrupt()
        thread.interrupt();
        System.out.println("中断线程,thread " + thread.getId() + " :" + thread.isInterrupted());
    }

}

复制代码

执行代码,打印以下结果:

工作中...
工作中...
工作中...
工作中...
工作中...
工作中...
工作中...
工作中...
工作中...
工作中...
中断线程,thread 11 :true
线程执行完成
复制代码

在main()中调用线程本身的interrupt(),设置线程的中断标识isInterrupted()为true,最终成功结束了循环中断了线程的执行。

但是使用interrupt()时需要注意线程是否处于阻塞状态。当对处于阻塞状态的线程调用interrupt()方法时,会抛出InterruptException异常,而这个异常会清除中断标识,也就是会将中断标识重新修改为false。

我们可以通过以下代码来模拟这种情况:

public class CloseThread3 {

    public static class CreateThread3 extends Thread {


        @Override
        public void run() {
            System.out.println("thread 已创建" + currentThread().getId() + " :" + currentThread().isInterrupted());
            while (!currentThread().isInterrupted()) {
                System.out.println("工作中...");
                try {
                    //线程暂停3秒,模拟处于阻塞状态
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("抛出异常,thread " + currentThread().getId() + " :" + currentThread().isInterrupted());
                }
            }
            System.out.println("线程执行完成");
        }
    }

    public static void main(String[] args) {
        CreateThread3 thread = new CreateThread3();
        thread.start();
        //当前线程暂停5秒
        SleepUtils.second(5);

        System.out.println("开始尝试中断线程");
        //调用线程的interrupt()
        thread.interrupt();
        System.out.println("中断线程,thread " + thread.getId() + " :" + thread.isInterrupted());
    }

}
复制代码

执行代码,打印以下结果:

thread 已创建11 :false
工作中...
工作中...
开始尝试中断线程
中断线程,thread 11 :false
抛出异常,thread 11 :false
工作中...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at threadDemo.CloseThread3$CreateThread2.run(CloseThread3.java:15)
工作中...
工作中...
工作中...
.........(不断的打印工作中...)
复制代码

由此可见,当线程处于阻塞状态中,调用线程的interrupt()将线程的中断标识isInterrupted()设置为,但是当抛出InterruptException异常时,会清除中断标识,将中断标识重新修改为false,导致线程并不会被终止,而是一直执行下去。

为了解决这种情况,我们可以将本文所讲的这两种中断线程的方法组合使用,为线程添加一个中断标识变量,并且重写interrupt()方法:

public class CloseThread4 {

    public static class CreateThread4 extends Thread {

        //定义退出标志,true会一直执行,false会退出循环
        public volatile boolean isRun = true;

        @Override
        public void run() {
            System.out.println("thread 已创建" + currentThread().getId() + " :" + currentThread().isInterrupted());
            while (isRun && !currentThread().isInterrupted()) {
                System.out.println("工作中...");
                try {
                    //线程暂停3秒,模拟处于阻塞状态
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("抛出异常,thread " + currentThread().getId() + " :" + currentThread().isInterrupted());
                }
            }
            System.out.println("线程执行完成");
        }

        /**
         * 重写interrupt()
         */
        @Override
        public void interrupt() {
            System.out.println("中断标识isRun设置为false");
            this.isRun = false;
            super.interrupt();
        }
    }

    public static void main(String[] args) {
        CreateThread4 thread = new CreateThread4();
        thread.start();
        //当前线程暂停5秒
        SleepUtils.second(5);

        System.out.println("开始尝试中断线程");
        //调用线程的interrupt()
        thread.interrupt();
        System.out.println("中断线程,thread " + thread.getId() + " :" + thread.isInterrupted());
    }

}

复制代码

执行代码,打印以下结果:

thread 已创建11 :false
工作中...
工作中...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at threadDemo.CloseThread4$CreateThread4.run(CloseThread4.java:17)
开始尝试中断线程
中断标识设置为false
中断线程,thread 11 :true
抛出异常,thread 11 :false
线程执行完成
复制代码

通过自定义中断标识和线程isInterrupted()中断标识组合使用,便解决了线程处于阻塞中调用interrupt()导致isInterrupted()被清除的问题。