Java多线程 - 停止线程

停止线程看起来非常简单,但是必须要做好防范措施,以便达到预期效果。
停止一个线程可以使用Thread.stop()方法,但是最好不要用它,虽然它确实可以停止一个线程,但是最好不要用它,因为它是不安全的,而且已经被弃用作废的,将来Java版本中,这个方法将不可用或者不被支持。
Java中有三种方式可以终止线程:
a. 使用退出标志,使线程正常退出,就是当run()执行完毕后线程终止。
b. 使用stop方法强行终止,已过期不推荐。stop和suspend及resume一样,都是过去作废的方法,使用它们将产生不可预料的结果。
c. 使用interrupt方法中断线程。

1. 停止不了的线程
使用interrupt()方法可以停止线程,但并不会像for + break那样马上停止。调用interrupt()方法仅仅是在当前线程中打了一个停止的标志,并不是真的停止线程。

public class MyThread6 extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
            System.out.println(i);
        }
    }

    public static void main(String[] args) throws Exception {
        MyThread6 myThread6 = new MyThread6();
        myThread6.start();
        Thread.sleep(2000);
        myThread6.interrupt();
    }
}

输出结果:

... // 从0开始
499996
499997
499998
499999

可以看到线程并没有停止,一直执行结束。

2. 判断线程是否是停止状态
Thread类中提供了两种方法判断线程是否停止:
this.interrupted() 测试当前线程是否已经中断,执行完后具有将状态标志清除为false的功能,“当前线程”指执行interrupted()方法的线程;
this.isInterrupted() 测试Thread线程对象是否已中断,执行完后不清除状态标志;

通过下面测试interrupted()方法可以清除状态标志:

public class MyThread7 extends Thread{

    public void run() {
        for (int i = 0; i < 500000; i++) {
            System.out.println(i);
        }
    }

    public static void main(String[] args) throws Exception {

        MyThread7 myThread7 = new MyThread7();
        myThread7.start();
        Thread.sleep(2700);
        myThread7.interrupt();

        System.out.println("是否停止:" + myThread7.interrupted());
        System.out.println("是否停止:" + myThread7.interrupted());
    }

}

输出结果:

...
490013
是否停止:false
是否停止:false
490014
490015
...

从打印结果来看,线程并没有停止,这也就证明了interrupted()方法的解释:测试当前线程是否已经中断。这里的“当前线程”是main线程,它从未中断过,所以打印两个false。

我们来看下interrupted()方法的源码,可以更加证明这里的“当前线程”就是main线程。

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

上面为何输出两个false?官方文档中对interrupted的解释是:测试当前线程是否已经中断,线程的中断状态由该方法终止。换句话说,如果连续两次调用该方法,则第二次将返回false(在第一个调用已清除了其中的状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

介绍完interrupted()后,我们来看下isInterrupted()方法。这个方法的声明如下:

public boolean isInterrupted() {
    return isInterrupted(false);
}

/**
 * Tests if some Thread has been interrupted.  The interrupted state
 * is reset or not based on the value of ClearInterrupted that is
 * passed.
 */
private native boolean isInterrupted(boolean ClearInterrupted);

我们看到这个方法不是static的,我来看一段测试代码:

public class MyThread7 extends Thread{

    public void run() {
        for (int i = 0; i < 500000; i++) {
            System.out.println(i);
        }
    }

    public static void main(String[] args) throws Exception {

        MyThread7 myThread7 = new MyThread7();
        myThread7.start();
        Thread.sleep(2900);
        myThread7.interrupt();

        System.out.println("是否停止:" + myThread7.isInterrupted());
        System.out.println("是否停止:" + myThread7.isInterrupted());
    }

}

输出结果:

...
495530
495531
是否停止:true
是否停止:true
495532
495533
...

从结果来看,isInterrupted()并没有清除状态标志,打印了两个true。

3. 能停止的线程 —— 异常法
停止线程需要interrupt()方法和interrupted()配合使用。来看一段测试的代码:

public class MyThread8 extends Thread{

    public void run() {

        for (int i = 0; i < 5000; i++) {

            System.out.println(i);
            if (this.interrupted()) {
                System.out.println(this.getName() + "线程中断了,退出循环");
                break;
            }
        }
    }

    public static void main(String[] args) throws Exception {

        MyThread8 myThread8 = new MyThread8();
        myThread8.start();

        Thread.sleep(20);

        myThread8.interrupt();
    }

}

输出结果:

...
1416
1417
Thread-0线程中断了,退出循环

使用上面停止线程的方法,虽然for循环里面不再执行,但是for下面的代码还会继续执行的,上面的停止线程只是跳出了for循环而已。
如何正确的停止线程呢?看下面测试代码:

public class MyThread8 extends Thread{

    public void run() {

        try {
            for (int i = 0; i < 5000; i++) {

                System.out.println(i);
                if (this.interrupted()) {
                    System.out.println(this.getName() + "线程中断了,退出循环");
                    throw new InterruptedException();

                }
            }
            System.out.println("我在for下面");
        } catch (InterruptedException e) {
            System.out.println(this.getName() + "线程已经中断~~~");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        MyThread8 myThread8 = new MyThread8();
        myThread8.start();

        Thread.sleep(10);

        myThread8.interrupt();
    }

}

输出结果:

...
770
771
Thread-0线程中断了,退出循环
Thread-0线程已经中断~~~
java.lang.InterruptedException
    at threadtest.MyThread8.run(MyThread8.java:13)

我们看到线程中断退出for循环,for后面的代码也没有执行,这就是使用异常停止线程的方法。

4. 在沉睡中停止线程
线程进入睡眠后被停止会是什么效果呢?看下面测试代码:

public class MyThread9 extends Thread{

    @Override
    public void run() {

        try {
            System.out.println("run 开始执行!");
            Thread.sleep(2000);
        } catch (Exception e) {
            System.out.println(this.getName() + "在沉睡中被停止~~" + this.isInterrupted());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        MyThread9 myThread9 = new MyThread9();
        myThread9.start();

        Thread.sleep(1000);
        myThread9.interrupt();

    }

}

输出结果:

run 开始执行!
Thread-0在沉睡中被停止~~false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at threadtest.MyThread9.run(MyThread9.java:10)

可以看到现在的确可以再沉睡中被停止,停止后线程的中断状态清除,变为false。
上面测试的是先睡眠再停止,下面是先停止再睡眠也可以停止线程:

public class MyThread9 extends Thread{

    @Override
    public void run() {

        try {
            for (int i = 0; i < 10000; i++) {

            }
            System.out.println("run 开始执行!");
            Thread.sleep(2000);
        } catch (Exception e) {
            System.out.println(this.getName() + "在沉睡中被停止~~" + this.isInterrupted());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        MyThread9 myThread9 = new MyThread9();
        myThread9.start();

        myThread9.interrupt();

    }
}

输出结果:

run 开始执行!
Thread-0在沉睡中被停止~~false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at threadtest.MyThread9.run(MyThread9.java:13)

5. 能停止的线程 —— 暴力停止
使用stop()方法停止线程则是非常暴力的。使用stop()停止线程测试如下:

public class MyThread10 extends Thread{

    @Override
    public void run() {
        int i = 0;
        try {
            while(true) {
                System.out.println(i++);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println("使用stop停止了线程" + this.getName());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {


        try {
            MyThread10 myThread10 = new MyThread10();
            myThread10.start();
            Thread.sleep(5000);
            myThread10.stop();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

输出结果:

0
1
2
3
4
5

调用stop()方法时会抛出异常,通常情况下,此异常不需要显示地捕捉。

public class MyThread11 extends Thread{

    @Override
    public void run() {
        try {
            this.stop();
        } catch (ThreadDeath e) {
            System.out.println(this.getName() + "通过stop停止了!");
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {

        MyThread11 myThread11 = new MyThread11();
        myThread11.start();
    }
}

输出结果:

Thread-0通过stop停止了!
java.lang.ThreadDeath
    at java.lang.Thread.stop(Thread.java:758)
    at threadtest.MyThread11.run(MyThread11.java:8)

stop()已经被作废,因为如果强制让线程停止则可能使一些清理性的工作得不得完成。另一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步处理,出现不同步的情况。
看下面测试因stop()强制停止线程而导致数据不同的情况:

public class MyThread12 extends Thread{

    private static Person person;

    @Override
    public void run() {
        try {
            person.setAge(19);
            Thread.sleep(5000);
            person.setName("赵雅芝");
        } catch (InterruptedException e) {
            System.out.println(this.getName() + "线程停止");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        person = new Person();
        person.setAge(18);
        person.setName("林青霞");

        System.out.println(person);

        MyThread12 myThread12 = new MyThread12();
        myThread12.start();

        Thread.sleep(1000);
        myThread12.stop();

        System.out.println(person);
    }

}

输出结果:

Person [age=18, name=林青霞]
Person [age=19, name=林青霞]

由于stop()方法已经在JDK中表明是“过期/作废”的方法,显然它在功能上有缺陷,所以不建议在程序中使用stop()方法。
再来对比下使用interrupt()停止线程后,数据是不是同步的:

public class MyThread14 extends Thread{

    private static Person person;

    @Override
    public void run() {
        try {
            person.setAge(19);
            Thread.sleep(5000);
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
            person.setName("赵雅芝");
        } catch (InterruptedException e) {
            System.out.println(this.getName() + "线程停止");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        person = new Person();
        person.setAge(18);
        person.setName("林青霞");

        System.out.println(person);

        MyThread12 myThread12 = new MyThread12();
        myThread12.start();

        Thread.sleep(1000);
        myThread12.interrupt();

        System.out.println(person);
    }
}

输出结果:

Person [age=18, name=林青霞]
Exception in thread "Thread-0" java.lang.NullPointerException
    at threadtest.MyThread12.run(MyThread12.java:10)
Person [age=18, name=林青霞]

6. 使用return停止线程
使用方法interrupt()和return结合使用也能实现停止线程的效果。看下面例子:

public class MyThread13 extends Thread{

    @Override
    public void run() {

        try {

            while(true) {
                System.out.println("while循环...");
                Thread.sleep(1000);
                return;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("while循环结束");
    }

    public static void main(String[] args) throws Exception {

        MyThread13 myThread13 = new MyThread13();
        myThread13.start();
        Thread.sleep(2000);
        myThread13.interrupt();
    }
}

输出结果:

while循环...

从输出结果看没有输出“while循环结束”,因为线程已经停止了。
不过还是建议使用抛异常的方法来实现停止线程,因为在catch块中还可以继续将异常向上抛,使线程停止的事件得以传播。

上述内容总结自《Java多线程编程核心技术》一书。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值