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

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

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

目录
相关文章
|
16天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
99 2
|
16天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
46 1
|
1月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
1月前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
52 3
|
14天前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
14天前
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
14天前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程
|
3月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
65 1
|
3月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
45 3
|
3月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
29 2