线程的停止方式(stop()、interrupt()、isInterrupted()、interrupted() 方法的使用)

本文详细介绍了Java中三种终止线程的方法:使用退出标志、stop方法(不推荐)和interrupt方法。并提供了代码示例说明如何正确使用这些方法。

在Java中有以下3种方法可以终止正在运行的线程:

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止

  2. 使用 stop 方法强行终止线程(不推荐使用该方法,因为stop和suspend以及resume,都是已作废过期的方法)

  3. 使用 interrupt 方法终止线程

一、使用退出标志终止线程

程序正常执行完成 run 方法后,线程自动终止。但有些场景中,需要 run 方法循环执行,如聊天室程 序中,可以启用在run方法中一直轮询是否接收到消息。这样线程就一直处于就绪或者运行状态,若要在某个条件下终止线程,则可以在线程外部修改线程的状态位,从而实现线程的终止。

public class ServerThread extends Thread {
    //volatile修饰符用来保证其它线程读取的总是该变量的最新的值
    public volatile boolean exit = false; 

    @Override
    public void run() {
        ServerSocket serverSocket = new ServerSocket(8080);
        while(!exit){
            serverSocket.accept(); //阻塞等待客户端消息
            ...
        }
    }
    
    public static void main(String[] args) {
        ServerThread t = new ServerThread();
        t.start();
        ...
        t.exit = true; //修改标志位,退出线程
    }
}

二、使用 stop 方法强行终止线程

stop() 方法可以停止一个正在运行的线程,它会立即释放资源,是不安全的,可能会导致一下清理性的工作得不到完成,如:关闭文件,关闭连接等,这个方法在Java中已废用。
使用 stop 方法的存在的问题:

  1. 调用 stop 方法时,会抛出 java.lang.ThreadDeath 异常,但在通常情况下,此异常不需要显式的捕获。
  2. 方法 stop() 已经被作废,因为如果强制让线程停止则可能会使一些清理性的工作得不到完成。另外一种情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。
    使用 stop() 释放锁将会给数据造成不一致性的结果。如果出现这样的情况,程序处理的数据可能遭受到破坏,最终导致程序执行的流程错误。
class SynchronizedObject {

    private String username = "a";

    private String password = "aaa";

    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) throws InterruptedException {
        this.username = username;
        Thread.sleep(10000);
        this.password = password;
    }
}


class MythreadWithStop implements Runnable {
    private SynchronizedObject synchronizedObject;

    public MythreadWithStop(SynchronizedObject synchronizedObject) {
        this.synchronizedObject = synchronizedObject;
    }

    @Override
    public void run() {
        try {
            synchronizedObject.printString("b", "bbb");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class StopTestRun{
    public static void main(String[] args) throws InterruptedException {
        SynchronizedObject synchronizedObject = new SynchronizedObject();
        MythreadWithStop mythread = new MythreadWithStop(synchronizedObject);
        Thread thread = new Thread(mythread);
        thread.start();

        Thread.sleep(500);
        thread.stop();

        System.out.println(synchronizedObject.getUsername() + "  " + synchronizedObject.getPassword());
    }
}

分析:
线程 MythreadWithStop 执行完 this.username = username 后,此时username = b,password = aaa,调用 sleep() 方法让当前线程睡眠 10s,线程睡眠时,释放CPU资源,Main线程获取到CPU调度获取到时间片,Main线程继续往下执行,调用 stop() 方法,释放 thread 线程。printString() 方法不再往下执行
Main方法中最后的打印结果为:b aaa

三、使用 interrupt 方法停止线程

调用 interrupt() 方法后,并不会像使用 for + break 一样跳出循环或者结束语句的执行,它仅仅只是在当前线程中打了一个停止标记,并不是真的停止线程。
要通过使用 interrupt() 方法停止线程,需要先了解 Thread.interrupted 和 this.isInterrupted() 这两个方法。这两个方法的作用都是用于判断线程是否处于停止状态。
1)Thread.interrupted() 测试当前线程是否已经中断,执行后具有将状态标志置清除为 false 的功能。(Thread 类里的静态方法)
2)this.isInterrupted() 测试线程是否已经中断,但不清楚状态标志。(实例方法)
(从这里可以得到个小总结,Thread类的静态方法都是作用于当前线程,如 Thread.sleep()、Thread.interrupt(),而实例线程方法只作用于实例线程)
测试:

/**
 * @ProjectName: java8
 * @Package: interrupt
 * @ClassName: interruptRun
 * @Author: chenziyu1@kungeek.com
 * @Description: ${description}
 * @Date: 2020/7/20 16:49
 * @Version: 1.0
 */
class InterruptedObject extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
            System.out.println(i);
        }
    }
}


public class interruptRun {
    public static void main(String[] args) throws InterruptedException {
        InterruptedObject thread = new InterruptedObject();
        thread.start();
        Thread.sleep(1000);
        //调用 interrupt 方法前,获取 thread 线程的状态
        boolean t0 = thread.isInterrupted();

        //调用 interrupt 方法,给 thread 线程打上一个停止标志
        thread.interrupt();

        //当前线程状态
        boolean t1 = Thread.interrupted();

        //给当前线程,即 Main线程打上一个停止标志
        Thread.currentThread().interrupt();

        //获取当前线程的状态,获取后重置停止标志为 false
        boolean t2 = Thread.interrupted();

        //再次获取当前线程状态
        boolean t3 = Thread.interrupted();

        //获取 thread 线程的状态
        boolean t4 = thread.isInterrupted();
        //获取 thread 线程的状态
        boolean t5 = thread.isInterrupted();

        System.out.println(t4);
    }
}

debug 测试结果:
debug测试结果

结果分析:

由于 t0 是在调用 interrupt() 方法前获取状态,所以 t0 = false

t1 是在 thread 线程调用 interrupt() 方法后获取状态,由于 Thread.interrupted() 方法只能获取当前线程的状态,而此时的当前线程是 Main 线程,Main线程正处于调度状态,且thread.interrupt() 仅给 thread 线程打上中断标志,而Main线程没有,所以 interrupt() 方法返回 false

t2 是在 Thread.currentThread.interrupt() 方法后获取状态,由于Thread.currentThread.interrupt() 作用是给当前线程打上中断标志(即 Main线程),所以 Thread.interrupted() 方法返回 true

t3 是在 Thread.interrupted() 方法后再次获取状态,由于Thread.interrupted() 方法具有清除中断状态的作用,所以 t3 再次获取状态时,返回 false

t4 由于是在 thread.interrupt() 方法后被调用,thread.interrupt() 会设置 thread 线程的中断标志为 true,所以 t4 = true

t5 由于 thread.interrupt() 方法不具有清除中断状态的作用,所以 t5 = true

注意区分停止的是当前线程还是指定线程,指定线程需要用指定的实例线程调用 interrupt() 方法,而停止当前线程需要调用 Thread 类的静态方法 interrupt()

3.1 使用 interrupt() 和 return 停止线程

原理:调用interrupt() 方法给线程标记中断标记,在 run() 方法中利用 isInterrupted()/interrupted() 获取这个中断标记是否是 true,若为 true,则直接 return
例子:

class InterruptedObject extends Thread{
    @Override
    public void run() {

        for (int i = 0; i < 500000; i++) {
            if (this.isInterrupted()){
                System.out.println(Thread.currentThread().getName() + "线程停止了");
                return;
            }
            System.out.println(i);
        }
    }
}

public class interruptRun {
    public static void main(String[] args) throws InterruptedException {
        InterruptedObject thread = new InterruptedObject();
        thread.start();
        Thread.sleep(1000);


        //调用 interrupt 方法,给 thread 线程打上一个停止标志
        thread.interrupt();
    }
}
3.2 使用 interrupt() 和抛异常法停止线程

由于代码中出现过多的 return; 会造成对代码的污染,而使用抛异常的方法时,在catch 块中可以对异常的信息进行相关的处理,而且使用异常流能更好、更方便地控制程序的运行流程。实现与 return相似,把return; 处替换成 throw XxxException() 即可。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值