Java多线程之停止线程

停止线程

这里我们将学习如何更好地停止一个线程。

停止线程,就是在线程正在处理指定任务的过程中,人为的不让它在继续执行下去,放弃当前的操作。停止线程看起来很简单,但是要想达到预期效果,就必须做好防范措施。

停止线程的3种方式:

1.使用退出标志,是线程正常退出,也就是执行完run方法再停止。

2.使用stop方法停止

3.使用interrupt方法中断线程

1.7.1 停止不了的线程

调用interrupt方法仅仅只是给线程一个停止标记,而不是真正的停止线程。

public class InterruptThread extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 50000; i++) {
            System.out.println("i = " + (i+1));
        }
    }
}

public class InterruptThreadTest {
    public static void main(String[] args){

        try {
            InterruptThread it = new InterruptThread();
            it.start();
            Thread.sleep(2000);
            it.interrupt();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

从运行结果上看,该线程并没有真的停止,而是让循环执行完了,这也就说明了interrupt方法不能真正的直接让线程停止,而是给线程打上了一个停止的标记。那么如何知道interrupt方法是给线程一个停止的标记呢?接下来,学习两种判断线程停止状态的方法来验证这个结论。

1.7.2判断线程是否是停止状态

判断线程停止状态有2种方法:

1.this.interrupted():测试当前线程是否已经中断

2.this.isInterrupted():测试线程是否已经中断

首先,看看interrupted方法:

public class InterruptedMethod extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 500000; i++) {
            System.out.println("i = " + (i + 1));
        }
    }
}

public class InterruptedMethodTest {
    public static void main(String[] args) {

        try {
            InterruptedMethod im = new InterruptedMethod();
            im.start();
            im.sleep(1000);
            im.interrupt();
            System.out.println("是否停止1? = "+ im.interrupted());
            System.out.println("是否停止2? = "+ im.interrupted());//其实这里是静态方法
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("end!");
    }
}

从运行结果可以看到,线程thread并未停止,那么到底判断的是谁的停止状态呢?从代码中看,im.interrupt语句是让im这个线程对象有了一个停止的标记。而interrupted方法是判断当前线程是否有停止标记,当前线程其实是main线程,没有给main线程停止标记,所以两次判断都是false。

public class Run2{
    public static void main(String[] args){
        Thread.currentThread().interrupt();
        System.out.println("是否停止1? = "+Thread.interrupted());
        System.out.println("是否停止2? = "+Thread.interrupted());
        System.out.println("end!");
    }
}

从这次的运行结果分析,为什么一个是true,一个是false呢?Thread.currentThread()返回的是当前线程,它再调用interrupt方法,是给当前线程main一个停止标记。判断方法interrupted判断当前线程是否停止,返回true,表示main线程已经有了停止的标记,第二次为什么打印false呢?是因为interrupted方法会清除这个状态标记。

注意事项:interrupted方法判断当前线程是否停止,具有清除状态的功能。

再来看看,isInterrupted方法:

public class IsInterruptedMethodTest {
    public static void main(String[] args) {

        try {
            InterruptedMethod im = new InterruptedMethod();
            im.start();
            Thread.sleep(2000);
            im.interrupt();
            System.out.println("是否停止1? = " + im.isInterrupted());//
            System.out.println("是否停止2? = " + im.isInterrupted());

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("end");
    }
}

从运行结果上看到打印了2个true,也就是说isInterrupted方法没有清除状态的功能。并且它作用的是调用它的线程对象是否有停止标记,而不是判断当前线程是否有停止标记。

总结:

1.interrupt方法时给线程一个停止标记,而不能真的停止线程

2.interrupted方法是判断当前线程是否有停止标记,并且具有清除状态的功能

3.isInterrupted方法是判断线程对象是否有停止标记,没有清除状态的功能。

1.7.2能停止的线程----异常法

有了前面的学习,我们来使用一下上面的方法:

public class StopThreadUseException extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 500000; i++) {
            if(this.interrupted()){
                System.out.println("已经是停止状态了,我要退出了");
                break;//这里的break只是跳出当前for循环,如果for循环后还有语句就继续执行,证明interrupt方法只是标记了线程的状态
            }
            System.out.println("i = " + (i+1));
        }
        //如果for循环下面还有语句,还会继续执行
        System.out.println("我被输出,如果此代码是for又继续执行,线程并未停止!");
    }
}

public class StopThreadUseExceptionTest {
    public static void main(String[] args) {

        try {
            StopThreadUseException ste = new StopThreadUseException();
            ste.start();
            Thread.sleep(1000);
            ste.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

从运行结果看,这里的break只是跳出了for循环,而线程没有停止。那么怎么才能是线程真的停止呢?有一种方式就是异常法,抛出一个异常,让线程真的停止。

public class StopThreadUseException2 extends Thread{
    @Override
    public void run() {
        super.run();
        try{
            for (int i = 0; i < 500000; i++) {
                if(this.interrupted()){
                    System.out.println("已经是停止状态了,我要退出了!");
                    throw new InterruptedException();
                }
                System.out.println("i = " + (i+1));
            }
            System.out.println("我在for下面");
        }catch (InterruptedException e ){
            System.out.println("进入StopThreadUseException2.java类run方法中的catch了");
            e.printStackTrace();
        }
    }
}

public class StopThreadUseException2Test {
    public static void main(String[] args) {
        try {
            StopThreadUseException2 stue = new StopThreadUseException2();
            stue.start();
            Thread.sleep(2000);
            stue.interrupt();
        }catch (InterruptedException e ){
            System.out.println("main catch");
            e.printStackTrace();
        }
    }
}

这里我们看到,线程才是真正的停止了。所以当我们要停止线程的时候,可以使用异常法。

1.7.4在沉睡中停止

如果在sleep状态下停止线程,会是什么效果呢?

public class StopInSleep extends Thread {
    @Override
    public void run() {
        super.run();
        try{
            System.out.println("run begin");
            Thread.sleep(200000);
            System.out.println("run end");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中停止,进入catch"+this.isInterrupted());
            e.printStackTrace();
        }
    }
}

public class StopInSleepTest {
    public static void main(String[] args) {

        try {
            StopInSleep sis = new StopInSleep();
            sis.start();
            Thread.sleep(100);
            sis.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
    }

 

在sleep状态下,停止线程会进入run方法的catch中,捕捉异常;并且会清除状态标识。上面是先使线程进入睡眠状态,再用interrupt停止。接下来看看先用interrupt停止,再sleep。

public class StopInSleep2 extends Thread{
    @Override
    public void run() {
        super.run();
        try{
            for (int i = 0; i < 100000; i++) {
                System.out.println( "i = " + (i + 1));
            }
            System.out.println("run begin");
            Thread.sleep(200000);
        }catch (InterruptedException e){
            System.out.println("先停止,遇到了sleep!进入catch");
        }
    }
}


public class StopInSleep2Test {
    public static void main(String[] args) {
        StopInSleep2 sis2 = new StopInSleep2();
        sis2.start();
        sis2.interrupt();
        System.out.println("end");
    }
}

 

1.7.5能停止的线程----暴力停止

public class Stop extends Thread {
    private int i = 0;

    @Override
    public void run() {
        try{
            while(true){
                i++;
                System.out.println("i = " + i);
                Thread.sleep(1000);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}


/*
* stop方法停止线程是暴力停止,不建议使用
* 并且stop方法时已过时的方法
* */
public class StopTest {
    public static void main(String[] args) {
        try{
            Stop stop = new Stop();
            stop.start();
            Thread.sleep(8000);
            stop.stop();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

根据代码分析,i在while循环中应该无限循环下去,但是让main线程暂停8秒后,main线程调用了stop()方法,暴力停止了线程,所以i的打印停止。stop方法可以停止线程。

注意事项:stop方法其实会抛出java.lang.ThreadDeath异常,但是在一般情况下,此异常不需要显示的捕捉。

1.7.7释放锁的不良后果

public class SynchronizedObject {
    private String username = "a";
    private String password = "aa";

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    synchronized public void printString(String username,String password){
        try{
            this.username = username;
            Thread.sleep(1000000);
            this.password = password;
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class MyThread extends Thread {
    private SynchronizedObject object;

    public MyThread(SynchronizedObject object){
        super();
        this.object = object;
    }

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



public class MyThreadTest {
    public static void main(String[] args) {
        try{
            SynchronizedObject object = new SynchronizedObject();
            MyThread mt = new MyThread(object);
            mt.start();
            Thread.sleep(500);
            mt.stop();
            System.out.println(object.getUsername() + " " + object.getPassword());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

 

从运行结果上看,打印的值不是我们预期的效果,是什么原因呢?

是因为调用了stop方法停止线程,会释放锁,导致多个线程在操作数据,出现了错误的情况。

所以还是不建议使用stop方法停止异常。

1.7.8 使用return停止线程

将方法interrupt()和return结合也能实现停止线程的效果:

public class MyThread extends Thread {
    @Override
    public void run() {
        while(true){
            if(this.isInterrupted()){
                System.out.println("停止了");
                return;
            }
            System.out.println("timer = "+System.currentTimeMillis());
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread mt = new MyThread();
        mt.start();
        Thread.sleep(2000);
        mt.interrupt();
    }
}

我们知道,return也可以马上结束当前函数,所以return和interrupt方法结合也可以停止线程。

总结:停止线程的三种方式:

1.interrupt方法并不能真的停止线程,而是给线程一个停止标记。

2.stop方法已经是过时的方法,不建议使用。

3.异常法,抛出一个异常,让线程真正的停止。

4.使用return和interrupt方法结合也可以停止线程。

但是在今后的使用中,还是建议使用抛异常法来停止线程,因为可以在catch中处理异常信息。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值