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

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

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),这是一种协作机制,能够使一个线程终止另一个线程的的工作。除此在外可以通过设置退出标志的方式控制线程的执行,线程内部检查变量状态,外部改变变量值可控制停止执行。

目录
相关文章
|
2月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
165 6
|
5月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
230 0
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
290 0
|
3月前
|
存储 Oracle Java
|
3月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
267 16
|
5月前
|
Java
创建线程的方法
Java中实现多线程有四种方式:1. 继承Thread类,简单但占用继承机会,耦合度高;2. 实现Runnable接口,推荐方式,任务与线程解耦,支持Lambda;3. 实现Callable接口配合FutureTask,可获取返回值和异常;4. 使用线程池(ExecutorService),企业推荐,管理线程生命周期,提升性能,支持多种线程池类型。
148 1
|
5月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
5月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
309 83
|
7月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
281 0
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
203 26

热门文章

最新文章