多线程编程(四)——如何停止一个线程?

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/swadian2008/article/details/99203151

目录

一、使用interrupt()方法停止线程

1、线程正常终止

正常终止的失败案例

2、线程异常终止

二、暴力终止

1、stop方法弊端(已废弃)

2、suspend 与 resume 方法的使用(已废弃)


1、使用退出标识(interrupt方法)。其一,使线程正常退出,也就是当run()方法完成后线程终止;其二,使用异常,通过抛出异常来终止操作。

2、暴力终止。使用stop和suspend及resume方法,当都是过期方法,非常容易引发线程安全问题,不推荐使用。

下边将对以上终止方法进行简单的示例。

一、使用interrupt()方法停止线程

使用interrupt()方法并不能使线程马上停止,它只是在当前线程打上了一个停止标记,并不是真正的停止线程。

如果要使线程停止,需要结合isInterrupted()方法进行判断,在进入判断的逻辑中添加中断操作。

1、线程正常终止

测试示例:

public class MyThread extends Thread {

    @Override
    public void run() {
        super.run();
        for(int i = 0;i<50000;i++){
            if(this.isInterrupted()){
                // 获取到中断标记,使用break终止循环,方法执行完毕,正常结束
                System.out.println("线程已经打上中断标记,执行停止...");
                break;
            }
            System.out.println("i="+(i+1));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000);
        // 给线程打上中断标记
        myThread.interrupt();
    }
}

注意:

1、如果只是用interrupt()方法是无法终止线程的。

2、在停止线程的操作当中,其实是手动实现中断逻辑的,interrupt()+isInterrupted()判断本身不会进行任何中断操作。

正常终止的失败案例

测试代码:

public class MyThread extends Thread {

    @Override
    public void run() {
        for(int i = 0;i<50000;i++){
            if(currentThread().isInterrupted()){
                // 获取到中断标记,使用break终止循环,方法执行完毕,正常结束
                System.out.println("线程已经打上中断标记,执行停止...");
                break;
            }
            System.out.println("第一层循环:i="+(i+1));
        }
        // 稍微修改方法,增加新的执行逻辑
        for(int i = 0;i<5;i++){
            System.out.println("第二层循环:i="+(i+1));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(100);
        // 给线程打上中断标记
        myThread.interrupt();
    }
}

执行结果:

当第一层循环结束后,第二层循环依然从头到尾的执行了。这是因为interrupt()方法+isInterrupted()判断逻辑+break,只控制了第一层循环,并没有影响到后边逻辑的执行。就像if()...elseif()一样,只是给出了判断的条件。因此使用interrupt()方法+isInterrupted()判断逻辑正常中断线程时,一定要考虑到全局,像本例的问题就是典型的控制不足。

2、线程异常终止

在正常终止失控的情况下,我们可以采用抛异常的方式来终止线程(推荐方法),此时,该线程的所有逻辑将不会再执行,线程如果持锁的话,也会对锁进行释放。下边示例是对上边示例的稍微改动,把break方法换成了抛异常。

测试代码:

public class MyThread extends Thread {

    @Override
    public void run() {
        try {
            for(int i = 0;i<50000;i++){
                if(currentThread().isInterrupted()){
                    // 获取到中断标记,不再使用break,而是抛出异常
                    System.out.println("线程已经打上中断标记,抛异常停止...");
                    throw new InterruptedException();
                }
                System.out.println("第一层循环:i="+(i+1));
            }
            // 新的循环
            for(int i = 0;i<5;i++){
                System.out.println("第二层循环:i="+(i+1));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(100);
        // 给线程打上中断标记
        myThread.interrupt();
    }
}

执行结果:

二、暴力终止

1、stop方法弊端(已废弃)

使用stop()方法能停止线程,但是如果强制让线程停止会带来以下问题:

(1)使程序的一些清理性工作得不到完成。

(2)stop()方法会释放锁,导致数据得不到同步处理。

测试代码:

新建MyService对象

public class MyService {

    private String username = "old_ussername";

    private String password = "old_password";

    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;
    }

    public synchronized void setValue(String username,String password){
        try {
            this.username = username;
            Thread.sleep(100000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

创建线程

public class MyThread extends Thread {

    public MyService myService;

    public MyThread(MyService myService){
        super();
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.setValue("new_username","new_Password");
    }

    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThread myThread = new MyThread(myService);
        myThread.start();
        Thread.sleep(500);
        // 暂停线程-暴力终止,此时会释放对象锁
        myThread.stop();
        // 打印对象的数据
        System.out.println("被破坏后的值——"+myService.getUsername()+":"+myService.getPassword());
    }
}

执行结果:

从结果可以看到,MyService对象的数据被修改一半以后,线程停止,main线程读到了该对象的脏值,发生了非常严重的线程安全问题。

2、suspend 与 resume 方法的使用(已废弃)

该方法目前已经被废弃,使用的弊端如下:

(1)如果处理不当,非常容易造成死锁问题。比如,执行该方法时,线程在暂停状态无法释放锁,如果该线程一直得不到恢复,那么其他线程在得不到锁的情况下,也将一直成等待状态,造成系统阻塞和瘫痪。

(2)数据得不到同步处理。如果有多个线程,多个锁对象,当其中一个线程在修改数据时停止,其他线程可能读到该线程修改了一半的脏数据,引发线程安全问题。

替换解决方案:使用wait()/notify(),将在后文详述。

猜你喜欢

转载自blog.csdn.net/swadian2008/article/details/99203151