停止及中断运行的线程

停止线程的方式

在 Java 中停止线程的方式有三种:
1、线程的 run 方法执行到最后,线程正常结束并退出;
2、使用 Thread 类提供的 stop 方法去停止一个正在运行的线程,该方法由于存在安全问题已被标记为过时,不推荐使用了;
3、使用 Java 中推荐的方式,即使用线程中断机制,去中断线程;

三种方式介绍

1、run 方法自行结束,这个是线程正常执行完后结束,没什么值得说的;

2、Thread 类的 stop 方法;

这个是一个在 Java 中被标记为过时的方法,因为该方法有线程安全方面的风险,因此已经不推荐使用了;具体原因可以参考源码中的解释,已经写的非常清楚了,下面举个例子说明:

public static void main(String[] args) throws InterruptedException {
        TestObject object = new TestObject(1, 1);
        TestRunnable testRunnable = new TestRunnable(object);
        Thread t1 = new Thread(testRunnable);
        Thread t2 = new Thread(testRunnable);
        t1.start();
        t2.start();
//        t1.stop();
        t1.join();
        t2.join();
        System.out.println(object.getX() == object.getY());
        System.out.println(object.getX() );
        System.out.println(object.getY());
        System.out.println("main end");
    }

    private static class TestObject{
        private int x ;
        private int y ;

        TestObject(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }

    private static class TestRunnable implements Runnable {
        private final Object lock = new Object();
        private final TestObject object;
        TestRunnable(TestObject object) {
           this.object = object;
        }
        @Override
        public void run() {
            synchronized (lock){
                object.setX(object.getX() + 1);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
               object.setY(object.getY() + 1);
            }
        }
    }

结果

false
3
2

如上 demo,基本上可以说明 stop 的线程安全问题了。在上面的示例中,runnable 任务分别将对象 TestObject 中的属性 x 和属性 y 加1,中间休息了 100 毫秒,模拟执行其他操作,在正常情况下他们是同时加1的。在不调用 stop 方法时,两个属性结果一致,都是3,但是当调用线程 t1 调用了 stop 方法,属性 x 值加1了,由于 y 得值还没来得及加 1 线程停止了,导致本来是原子的操作两个属性,最后没能一起增加,导致了线程安全的问题。
因此 stop 方法在 Java 中标记为过时,并且也不推荐使用了。现在推荐的是使用线程的中断机制。

3、线程的中断机制介绍;

Java 中提供的线程中断机制,实际上是一种线程之间的协作方式;这种中断和 stop 有着本质的区别,stop 是直接中断线程,而 Java 中的中断机制,其实是给线程设置一个中断的标识,当前线程并不会被真的终止执行,因此需要线程自己去根据的标识处理相关逻辑。下面介绍线程中断机制中,三个重要的方法。

a、interrupt 方法

这个方法可以给线程设置中断的标识,然后可以根据中断的标识自行处理。例如线程 A 和主线程,主线程调用线程 A 的 interrupt 方法,这个时候就把线程 A 的中断标识设为了 true,但是这个时候线程 A 仍在执行,需要线程 A 根据标识做处理,结束自己的执行。

public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            while(true) {
                System.out.println("threadA is running");
                //如果中断标识为true,则退出循环,threadA 执行结束
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("threadA is stop");
                    break;
                }
            }
        });
        threadA.start();
        Thread.sleep(100);
        threadA.interrupt();
        threadA.join();
        System.out.println("main is end");
    }

结果

threadA is running
threadA is running
threadA is running
threadA is running
threadA is running
threadA is running
threadA is stop
main is end

在调用 interrupt 方法时,还有一个需要注意的,当线程内调用了 wait、sleep、join 方法而被挂起时,当前线程会在这些地方抛出,InterruptedException 异常后返回。例如如果线程 A 中有调用 wait 或者 sleep 或者 join 方法,这个时候主线程调用了 interrupt 方法,则线程 A 会在调用这些方法的地方,抛出 InterruptedException 异常,需要程序自己处理该异常,保证线程安全切正确的执行。

public static void main(String[] args) throws InterruptedException {
        TestObject object = new TestObject(1, 1);
        TestRunnable testRunnable = new TestRunnable(object);
        Thread t1 = new Thread(testRunnable);
        Thread t2 = new Thread(testRunnable);
        t1.start();
        t2.start();
        t1.interrupt();
        t1.join();
        t2.join();
        System.out.println(object.getX() == object.getY());
        System.out.println(object.getX() );
        System.out.println(object.getY());
        System.out.println("main end");
    }

    private static class TestObject{
        private int x ;
        private int y ;

        TestObject(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }

    private static class TestRunnable implements Runnable {
        private final Object lock = new Object();
        private final TestObject object;
        TestRunnable(TestObject object) {
           this.object = object;
        }
        @Override
        public void run() {
            synchronized (lock){
                int x = object.getX();
                int y = object.getY();
                try {
                    object.setX(x + 1);
                    Thread.sleep(100);
                    object.setY(y + 1);
                } catch (InterruptedException e) {
                    object.setY(y);
                    object.setX(x);
                }
            }
        }
    }

结果

true
2
2

上面示例使用了说明 stop 不安全的例子,稍微修改了代码,由于示例中有调用 sleep 方法,因此处理了 InterruptedException 异常,将 stop 改为了 interrupt 方法,从而避免了线程不安全的问题,该示例了没有对线程的中断标识做处理,因为该示例处理的意义不大,这个根据具体使用场景处理就行。

b、isInterrupted 方法

这个方法用于检查当前线程的中断标识,如果当前线程标识为中断,则返回 true 反之则返回 false,这个方法的使用方式,在上面的示例中已经给出,这里就不提供具体的代码了。

c、interrupted 方法

这个方法也是用于检查线程的中断标识,如果中断则返回 true,反之则是 false。但是和 isInterrupted 方法有两点不同;第一如果发现线程被中断会返回 true,同时会清除线程的中断标识,这个时候线程标识变成了非中断;第二这个方法获取的是当前执行线程的线程状态,并不是被调用线程的状态,具体看下面的代码;

/**
     * Tests whether the current thread has been interrupted.  The
     * interrupted status of the thread is cleared by this method.  In
     * other words, if this method were to be called twice in succession, the
     * second call would return false (unless the current thread were
     * interrupted again, after the first call had cleared its interrupted
     * status and before the second call had examined it).
     *
     * 

A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return true if the current thread has been interrupted; * false otherwise. * @see #isInterrupted() * @revised 6.0 */ public static boolean interrupted() { return currentThread().isInterrupted(true); }

上面是该方法的 JDK 源码,它返回的是当前执行的线程的中断状态;

public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            System.out.println("threadA start interrupted :" + Thread.currentThread().isInterrupted());
            while (!Thread.interrupted()) {
            }
            System.out.println("threadA stop interrupted :" + Thread.currentThread().isInterrupted());
        });
        threadA.start();
        threadA.interrupt();
        threadA.join();
        System.out.println("main end");
    }

结果

threadA start interrupted :true
threadA stop interrupted :false
main end

总结

1、stop 是线程不安全的方法,不推荐使用;
2、interrupt 只是设置一个中断的标识,需要在线程里面处理相关逻辑;
3、interrupt 执行时,有 sleep、wait、join、的地方会抛出 InterruptedException 异常;
4、interrupted 和 isInterrupted 都可以获取线程的中断状态;
5、interrupted 获取的是当前线程的中断状态,并且会清除中断标识;

你可能感兴趣的:(停止及中断运行的线程)