多线程---停止线程

停止线程在java语言中并不像break,return那样干脆,需要一定的技巧性。

之前认为线程的停止很简单,一个interupt()方法就完成了,事实上这是完全错误的,线程的停止API中确实提供了一个简单的方法stop(),但是在新的API中已经被标记过时了,原因就是他不够安全。
在java中提供了2中方法用来终止正在运行的线程:
1)使用退出标志,使线程正常退出,也就是run()完成后线程终止
2)使用stop强行终止线程,但是不推荐,产生不可预料的后果
3)使用interrupt终止线程

  • interrupt()并不能停止线程
    可能我说了,你也不信,那就来个demo验证一下
public class A extends Thread {
    @Override
    public void run() {
        for(int j=0;j<50000;j++){
            System.out.println("j="+j);
        }

    }

}


public static void main(String[] args) {
        A a = new A();
        a.start();
        try {
            Thread.sleep(2000);
            a.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }

    }

看一下打印结果:
image_1b4dotc0e10261j0npcq1v2h1spf9.png-18.7kB

没少打印吧,这就说明interrupt()并不能停止线程,调用interrupt()仅仅是在当前线程中打了一个停止标记,并不是真的停止线程。

  • 正确判定线程的运行状态
    想要正确的停止线程,必须要知道线程处于生命周期的什么时期,必须要了解这几个API.
    1) interrupted();
    2) isInterrupted();

先看一下这2个的介绍:
image_1b4dpfp1d1pgmq0g174q1i9q1h3em.png-20.1kB

image_1b4dpgfdnt5p7fl1vb21um6s9m13.png-13.5kB

这里说的很清晰了,interrupted()测试当前线程是否中断,并且清除中断状态;isInterrupted()是测试线程是否已经中断,并不影响线程的状态;
下面来个demo说明一下:

    public static void main(String[] args) {
        A a = new A();
        a.start();
        try {
            Thread.sleep(2000);
            a.interrupt();
            System.out.println("interrupted:"+a.interrupted());
            System.out.println("interrupted:"+a.interrupted());
            System.out.println("is interrupted:"+a.isInterrupted());
            System.out.println("is interrupted:"+a.isInterrupted());

        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }

    }

打印结果
image_1b4dpqrvt1m3r1mjlni11i3l10521g.png-4.4kB

再来修改一下:

public static void main(String[] args) {
        A a = new A();
        a.start();
        try {
            Thread.sleep(2000);
            a.interrupt();
            Thread.currentThread().interrupt();
            System.out.println("interrupted:"+Thread.interrupted());
            System.out.println("interrupted:"+Thread.interrupted());
            System.out.println("is interrupted:"+a.isInterrupted());
            System.out.println("is interrupted:"+a.isInterrupted());

        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }

    }

再看一下打印:
image_1b4dq02lqmrf1sqefooddd1dh01t.png-4.1kB

这个结果就印证了API上对interrupted()的说明,是测试当前线程的状态,并清除中断状态。

分析:先看第一种情况,只是执行了a.interrupt(),虽然也执行了a.interrupted(),但是还是检测的当前线程,主线程(Main)一直没有中断,所以第一次的interrupted=false;
第二种情况:Thread.currentThread.interrupt()中断了主线程,所以第一次interrupted=true,由于他还有个作用清除当前线程的中断状态,所以第二次的时候interrupted=false;

在修改一下,看下isInterrupted

public class A extends Thread {
    @Override
    public void run() {
        for(int j=0;j<500000;j++){
            System.out.println("j="+j);
            if(this.isInterrupted()){
                System.out.println("is interrupted:"+this.isInterrupted());
                System.out.println("is interrupted:"+this.isInterrupted());
                break;
            }
        }

    }

}

public static void main(String[] args) {
        A a = new A();
        a.start();
        try {
            Thread.sleep(2000);
            a.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }

    }

看下打印结果:
image_1b4dtqtvl1h6a1k601bog65re7i2a.png-3.7kB
这里看到isInterrupted()并不会清除线程的终止状态,所以2次都会true,虽然状态是ture,只是跳出了循环,但是线程仍然会运行。

  • 异常法停止线程
    有了前面的检测线程状态的API,就可以为线程的停止,提供依据
public class A extends Thread {
    @Override
    public void run() {
        try{
            for(int j=0;j<500000;j++){
                System.out.println("j="+j);
                if(this.isInterrupted()){
                    throw new InterruptedException("thread is interrupted");
                }
            }
            System.out.println("for 之后运行");
            //do some
        }catch(Exception e){
            System.out.println(e.toString());
        }


    }

}

public static void main(String[] args) {
        try {
            A a = new A();
            a.start();
            Thread.sleep(1000);
            a.interrupt();

        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }

    }

看下打印结果
image_1b4dubulk16vc43819sm2nnrhs2n.png-7kB
线程确实停止了,for语句后的也没有执行

  • return停止线程
    将上面代码中的 throw new InterruptedException(“thread is interrupted”);改为return;也可以进行线程的终止,但是最好还是用异常法,因为catch块中的异常可以向上进行抛,使线程停止事件得以传播

  • 使用stop停止线程(不建议使用)
    先看一下使用会有什么样的不良后果,

public class SynObject {
    private String name="a";
    private String pwd="aaa";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    synchronized public void printString(String name,String pwd){
        try {
            this.name = name;
            Thread.sleep(100*1000);
            this.pwd = pwd;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

public class A extends Thread {
    private SynObject object;

    public A (SynObject object){
        this.object = object;
    }

    @Override
    public void run() {
        object.printString("b", "bbb");
    }

}

    public static void main(String[] args) throws Exception {
            SynObject object = new SynObject();
            A a = new A(object);
            a.start();
            Thread.sleep(500);
            a.stop();
            System.out.println("name:"+object.getName()+",pwd:"+object.getPwd());
    }

这个逻辑很简单,object属性是有进行初始化的,printString对属性进行了重新赋值,看下结果
image_1b4e0pa8o1ro5p3aoqd6p4176934.png-7.2kB

虽然线程是被及时停止了,但是获得的结果并不是我们预想的。stop()强行停止,可能使一些清理性的工作得不到完成,造成内存的泄漏;另外一个就是对锁定对象进行了“解锁”,导致数据得不到同步处理,出现数据不一致的问题;所以最好不要用

  • b

猜你喜欢

转载自blog.csdn.net/baidu_17508977/article/details/53767996