多线程编程之停止线程的几种方法

简介: 前面的文章线程以及线程的常用方法介绍了线程的使用。线程除了在执行处理完成其任务后会停止外,还可以通过一些方法进行干预停止其运行。停止一个线程意味着在线程处理完成任务之前结束其正在执行的操作。

1 前言


前面的文章线程以及线程的常用方法介绍了线程的使用。线程除了在执行处理完成其任务后会停止外,还可以通过一些方法进行干预停止其运行。停止一个线程意味着在线程处理完成任务之前结束其正在执行的操作。


在java中可以使用以下三种终止线程的方法。


2 正文


1、使用退出标志


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


当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。比如:


package com.jiangxia.chap1;
/**
 * 线程停止的方法之:使用退出标志
 * author:jiangxia
 * date:2021-04-15
 */
public class Demo12 {
    public static void main(String[] args) throws InterruptedException {
        Demo12Thread t = new Demo12Thread();
        t.start();
        Thread.sleep(2000);
        t.stopThread();
    }
}
class Demo12Thread extends Thread{
    private  boolean flag = true;
    @Override
    public void run() {
        try {
            while (flag){
                System.out.println("Time="+System.currentTimeMillis());
                Thread.sleep(1000);
            }
            System.out.println("线程执行结束");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
     /**
     * 定义一个停止线程的方法
     */ 
    public void stopThread(){
        //将标志位改为flase
        flag = false;
    }
}
复制代码


3cbdc0b6f4a049b89f171dd2e059a913~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


2、stop方法强制结束线程


java种提供了Thread.stop()方法停止一个线程,虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是已被废弃的方法。调用stop()方法时会抛出java.lang.ThreadDeath异常。官方文档对其的解释如下:


该方法天生是不安全的。使用thread.stop()停止一个线程,导致释放(解锁)所有该线程已经锁定的监视器(因沿堆栈向上传播的未检查异常ThreadDeath而解锁)。如果之前受这些监视器保护的任何对象处于不一致状态,则不一致状态的对象(受损对象)将对其他线程可见,这可能导致任意的行为。


package com.jiangxia.chap1;
/**
 * 停止线程之stop方法
 * author:jiangxia
 * date:2021-04-15
 */
public class Demo13 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Demo13Thread();
        t.start();
        Thread.sleep(2000);
        //该方法有个删除线表示已经被删除 不建议使用
        //stop方法会产生异常
        t.stop();
    }
}
class  Demo13Thread extends Thread{
    @Override
    public void run() {
        try{
            while(true){
                System.out.println("run方法 Time="+System.currentTimeMillis());
                Thread.sleep(1000);
            }
        }catch (InterruptedException e){
                e.printStackTrace();
        }catch (ThreadDeath threadDeath){
            System.out.println("进入catch块");
            threadDeath.printStackTrace();
        }
    }
}
复制代码


dacf1e3ef6a745be8a73464479894335~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


通过idea可以发现stop方法有个删除线,说明stop方法已经被作废,如果强制让线程停止有可能使一些清理性的工作得不到完成。另外一个原因是对锁定的对象进行『解锁』,导致数据得不同同步的处理,出现数据不一致性的问题。所以使用stop()方法停止线程则是非常暴力的,由于stop()方法以及在JDK中被标明为“过期/作废”的方法,所以它在功能上具有缺陷,所以不建议在程序张使用stop()方法。比如:


package com.jiangxia.chap1;
public class Demo14 {
    public static void main(String[] args) throws InterruptedException {
        Demo14Info info = new Demo14Info();
        Thread t = new Demo14Thread(info);
        t.start();
        Thread.sleep(200);
        t.stop();
        //因为stop方法结束了线程,所以只更新了username的值,而password的值还是之前的值没有被更新
        System.out.println("username="+info.getUsername()+":password="+info.getPassword());
    }
}
class  Demo14Thread  extends Thread{
    private Demo14Info info;
    public Demo14Thread(Demo14Info info){
        this.info = info;
    }
    @Override
    public void run() {
        info.updateInfo("张飞","1111111");
    }
}
class Demo14Info{
    private String username="刘备";
    private String password="222222";
    public String getUsername() {
        return username;
    }
    public String getPassword() {
        return password;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    synchronized  public void updateInfo(String username,String password){
        try {
            this.username = username;
            Thread.sleep(1000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
复制代码


f81743a5817c4bf19565fc3dc5f2748c~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


因为stop方法结束了线程,所以上述代码只更新了username的值,而password的值还是之前的值没有被更新。


3、使用interrupt方法中断线程


interrupt()其作用是中断此线程,注意此线程不一定是当前线程,而是值调用该方法的Thread实例所代表的线程,调用interrupt方法不会真正的结束线程,在当前线程中打上一个停止的标记,线程仍然会继续运行。


Thread类提供了interrupted方法测试当前线程是否中断,即检查中断的标志,返回一个boolean值并清除中断的状态,第二次再调用时中断状态已经被清楚,所以会返回false。


isInterrupted方法测试线程是否已经中断,不清除中断状态。


对这三个方法的总结可以归纳如下:


public void Thread.interrupt() // 无返回值
public boolean Thread.isInterrupted() // 有返回值
public static boolean Thread.interrupted() // 静态,有返回值
复制代码


比如:


package com.jiangxia.chap1;
/**
 * 停止线程之interrupt方法
 * author:jiangxia
 * date:2021-04-15
 */
public class Demo15 {
    public static void main(String[] args) {
        Thread thread = new Thread15();
        thread.start();
        //使用interrupt方法中断线程
        thread.interrupt();
    }
}
class Thread15 extends  Thread{
    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            System.out.println("run方法:i="+i);
        }
    }
}
复制代码


fb4210ce65504a1f9ca9d4ed89f9e15d~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


调用interrupt方法不会真正的结束线程,而是在当前线程上打上一个停止的标记。


package com.jiangxia.chap1;
/**
 * 停止线程之isInterrupted方法
 * author:jiangxia
 * date:2021-04-15
 */
public class Demo15 {
    public static void main(String[] args) {
        Thread thread = new Thread15();
        thread.start();
        //使用interrupt方法中断线程
        thread.interrupt();
        System.out.println("是否已经停止1:"+thread.isInterrupted());
        System.out.println("是否已经停止2:"+Thread.interrupted());
    }
}
class Thread15 extends  Thread{
    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            System.out.println("run方法:i="+i);
        }
    }
}
复制代码


43fb7ee0101140f6b56f697e616bc9db~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

9aa7f5cbceb744b8ba1f659627a4ec75~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


thread.isInterrupted方法是检查Thread15是否被打上停止的标记。


Thread.interrupted方法是检查主线程是否打上停止的标记。


interrupted()与isInterrupted()的唯一区别是,前者会读取并清除中断状态,后者仅读取状态。


测试当前线程是否已经中断,线程的中断的状态由方法清除,如果两次连续调用该方法,第二次调用将返回false。


package com.jiangxia.chap1;
public class Demo17 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thred17();
        //启动线程
        thread.start();
        //Thread.sleep(1000);
        thread.interrupt();
    }
}
class Thred17 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            if(this.isInterrupted()){
                System.out.println("已经是停止状态!");
                break;
            }
            System.out.println("i="+i);
        }
        System.out.println("这里是结束循环后的代码");
    }
}
复制代码


9c253bee2a714b21a768077044c8c1c3~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


如果线程中有sleep代码,不管是否进入到sleep的状态,如果调用了interrupt方法,都会产生异常信息。比如:


package com.jiangxia.chap1;
public class Demo17 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thred17();
        //启动线程
        thread.start();
        //Thread.sleep(1000);
        thread.interrupt();
    }
}
class Thred17 extends Thread{
    @Override
    public void run() {
        try{
            for (int i = 0; i <1000 ; i++) {
                if(this.isInterrupted()){
                    System.out.println("已经是停止状态!");
                    //如果使用break那么会导致其他线程不知道,所以在这里直接抛出异常
                    // break;
                    throw  new InterruptedException();
                }
                System.out.println("i="+i);
            }
            System.out.println("这里是结束循环后的代码");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
复制代码


e121873bd1db4457a7c7b6ee3ebfefd4~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


package com.jiangxia.chap1;
public class Demo18 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread18();
        t.start();
        Thread.sleep(20);
        t.interrupt();
    }
}
class Thread18 extends Thread{
    @Override
    public void run() {
        try {
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                String s = new String();
            }
            System.out.println("开始线程");
            Thread.sleep(20000);
            System.out.println("结束线程");
        }catch (InterruptedException e){
            System.out.println("异常进入catch代码块");
            e.printStackTrace();
        }
    }
    }
复制代码


0fda583d0d974b6194b985dee2f82bcf~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


3 总结


以上就是关于Java种如何停止一个线程的三种方法。在Java中没有提供任何机制来安全地终止线程。stop方法因为不安全已经被废弃了。但java提供了中断机制(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的的工作。除此在外可以通过设置退出标志的方式控制线程的执行,线程内部检查变量状态,外部改变变量值可控制停止执行。

目录
相关文章
|
1天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
8 3
|
3天前
|
监控 安全 Java
Java多线程编程的艺术与实践
【10月更文挑战第22天】 在现代软件开发中,多线程编程是一项不可或缺的技能。本文将深入探讨Java多线程编程的核心概念、常见问题以及最佳实践,帮助开发者掌握这一强大的工具。我们将从基础概念入手,逐步深入到高级主题,包括线程的创建与管理、同步机制、线程池的使用等。通过实际案例分析,本文旨在提供一种系统化的学习方法,使读者能够在实际项目中灵活运用多线程技术。
|
1天前
|
缓存 安全 Java
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文将深入探讨Java中的多线程编程,包括其基本原理、实现方式以及常见问题。我们将从简单的线程创建开始,逐步深入了解线程的生命周期、同步机制、并发工具类等高级主题。通过实际案例和代码示例,帮助读者掌握多线程编程的核心概念和技术,提高程序的性能和可靠性。
7 2
|
2天前
|
Java
Java中的多线程编程:从基础到实践
本文深入探讨Java多线程编程,首先介绍多线程的基本概念和重要性,接着详细讲解如何在Java中创建和管理线程,最后通过实例演示多线程的实际应用。文章旨在帮助读者理解多线程的核心原理,掌握基本的多线程操作,并能够在实际项目中灵活运用多线程技术。
|
3天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
19 3
|
21天前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
36 1
C++ 多线程之初识多线程
|
6天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
11 3
|
6天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
9 2
|
6天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
15 2
|
6天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
16 1