JAVA多线程基础篇 3、如何优雅结束一个线程

从线程状态机上看如何优雅停止线程 目录 如何优雅的终止线程 暂停线程的方法 RUNNALBE-->BLOCKED RUNNALBE_WAITING/TIMING_WAITING Lock等待锁,Condition等待条件 LockSupport.park Object.wait Thread.sleep() Thread.join() 其他 如何优雅的终止线程 Thread中提供了stop()等方法,... 阅读详情


线程的结束有几种类型

  • 寿终正寝,完成了使命,自然死亡。
  • 线程在执行的过程中,因为耗时过长或者各种原因被中断。

中断线程该如何处理呢?

1. 错误的方法

1.1 stop()方法不要使用

public class StopIsNotSafe {

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(()->{
            while (true) {
                System.out.println("我在换衣服");
                System.out.println("我在脱裤子");
                System.out.println("我在穿裤子");
            }
        });

        t1.start();
        Thread.sleep(10);
        t1.stop();
    }
}

运行结果,脱了裤子没有穿,程序运行到一半:

stop方法

哪怕该线程正在执行某个事务,锁定了某个对象,此时仍然会被粗暴中止,往往此时程序正在做着重要的事情啊,一旦中止本应该完整的逻辑就被破坏了。

        Object o = new Object();
        Thread t2 = new Thread(()->{
            synchronized (o) {
                while (true) {
                    System.out.println("t2 我在换衣服");
                    System.out.println("t2 我在脱裤子");
                    System.out.println("t2 我在穿裤子");
                }
            }
        });

        t2.start();
        Thread.sleep(10);
        t2.stop();

运行结果,脱了裤子没有穿,程序运行到一半:

stop方法2

1.2 suspend() 也不建议使用

很简单,因为suspend方法调用后,线程会被暂停,但是锁资源仍然没有释放。
万一忘记resume(),或者调用resume时出现了问题,就会出现死锁。
况且这个方法并不是中断,只是暂停。

public class ResumeIsNotSafe {

    public static void main(String[] args) throws InterruptedException {

        Object o = new Object();

        Thread t1 = new Thread(()->{
            for(int i=0;i<5;i++) {
                synchronized (o) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("t1 :"+ LocalDateTime.now());
                }
            }
        });
        t1.start();

        Thread.sleep(100);
        t1.suspend();
        Thread.sleep(10000);
        //如果resume失效了。则无法执行t2了。
        System.out.println("此时,t1进入暂停状态,但是它仍然持有锁");
        //t1.resume();

        Thread t2 = new Thread(()->{
            for(int i=0;i<5;i++) {
                synchronized (o) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("t2 :"+ LocalDateTime.now());
                }
            }
        });
        t2.start();
    }
}

执行结果,t2线程无法获得锁,死锁:

suspend忘记resume

2.正确的方法

2.1 使用volatile + 标志位

有句话叫做“凯撒的归凯撒,上帝的归上帝”,线程自己的事情,应该由线程自己控制。
我们可以在线程类内部设定一个flag字段,主线程通过该字段能够通知t1线程,线程监控该字段,当该字段为True,则线程正常运行,否则就停下来。

public class UsingFlagToStop {

    private static volatile boolean flag = true;

    public static void main(String[] args) throws InterruptedException {

        //线程t1每秒打印一次
        Thread t1 = new Thread(()->{
            while (flag) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1:"+ LocalDateTime.now());
            }
        });

        t1.start();
        Thread.sleep(4000);
        flag=false;
    }
}

执行结果如下,当主线程4秒后设置flag为false后,t1线程检测到标志被设置,于是停止了运行。

suspend忘记resume

不足之处 ,volatile标志位无法及时通知正在wait()和sleep()的线程。

2.2 使用线程的interrupt()方法

了解关于interrupt有关的三个方法。

  • void interrupt() ,例如t1.interrupt(),通过设置t1线程的中断标志通知线程。此时线程会接收到InterruptedException异常,从而进行控制。
  • boolean isInterrupted() 线程可以通过该方法查询自身的中断标志。
  • static boolean interrupted() ,该方法是静态方法,查看线程当前的中断的标志,如果是被打断状态,会重置恢复标志。

2.2.1 方式1、interrupt() 后进入Exception处理。

interrupt的java内置的解决方案,能够在wait()和sleep()时,及时通知,更加优雅有效。
线程t1 一开始运行后,就睡眠,通过调用t1.interrupt(),将t1打断,不用等那么久了。

public class InterruptToStop {

    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        Thread t1 = new Thread(()->{
            synchronized(o) {
                System.out.println(LocalDateTime.now()+" t1:等等我先喝口水上个厕所");
                try {
                    Thread.sleep(10000);
                    //对于正在wait的线程也可以中断哦。
                    //o.wait();
                } catch (InterruptedException e) {
                    System.out.println(LocalDateTime.now()+" t1:摸鱼的我,被打断了,赶紧善后。");
                }
                System.out.println(LocalDateTime.now()+" t1:事情忙完了。");
            }
        });

        Thread t2 = new Thread(()->{
            synchronized(o) {
                System.out.println(LocalDateTime.now()+" t2:终于轮到我上场了");
            }
        });

        t1.start();
        //确保t1先启动,拿到锁对象o。
        Thread.sleep(50);
        t2.start();
        t1.interrupt();
    }
}

运行结果显示,t1被打断后,进入异常处理环节,在这里实现更加优雅的打断。

interrupt

2.2.2 方式2、线程不断检查自身中断状态。

也可以通过线程检查自身interrupted状态来控制。
这种方式适用于线程内部没有调用wait、sleep等方法的情况下。因为在这种情况下,是没有对InterruptedException进行特殊处理。所以需要线程不断检查interrupted状态。

public class InterruptToStop2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            while (!Thread.interrupted()) {
                System.out.println("t1 处理事情 "+ LocalDateTime.now());
            }
            System.out.println("我被打断了,做后面的事情");
        });
        t1.start();
        Thread.sleep(1000);
        t1.interrupt();
    }
}

运行结果,t1检查到被interrupt,被中断。

interrupt2

2.3 如何打断正在抢锁(BLOCKED)状态的线程

interrupt()方法对于正在抢锁的线程是无能为力的。

做一个实验,线程进入blocked状态后,通过interrupt打断,没有效果。

public class InterruptToStop3 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        Thread t1 = new Thread(()->{
            synchronized (o) {
                System.out.println(LocalDateTime.now()+",t1:我获得了锁,我将占有10秒钟");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();

        //保证t1先执行
        Thread.sleep(50);

        Thread t2 = new Thread(()->{
            System.out.println(LocalDateTime.now()+",t2 我将争抢锁");
           synchronized (o) {
               System.out.println(LocalDateTime.now()+",t2:我终于抢到锁了");
           }
        });
        t2.start();

        Thread.sleep(50);
        System.out.println("t2 状态:"+ t2.getState());
        //此时无法打断。进入blocked状态。
        t2.interrupt();
    }
}

运行结果,t2非要拿到锁后继续执行。感觉是一个很执拗的线程。

interrupt和抢锁1

解决方案是,使用ReentrantLock锁对象,调用lockInterruptibly()方法加锁。

public class InterruptToStop4 {
    public static void main(String[] args)  {
        ReentrantLock o = new ReentrantLock();
        Thread t1 = new Thread(()->{
            try {
                o.lock();
                System.out.println(LocalDateTime.now()+",t1:我获得了锁,我将占有10秒钟");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {

                }
            } finally {
                o.unlock();
            }
        });
        t1.start();

        //保证t1先执行
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            //e.printStackTrace();
        }

        Thread t2 = new Thread(()->{
            System.out.println(LocalDateTime.now()+",t2 我将争抢锁");
            try {
                o.lockInterruptibly();
                System.out.println(LocalDateTime.now()+",t2 我终于拿到锁啦!!!");

            } catch (InterruptedException e) {
                System.out.println(LocalDateTime.now()+",t2 我被中断了!");
            }finally {
                if(o.isHeldByCurrentThread())
                o.unlock();
            }
        });
        t2.start();
        //保证t1先执行
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            //e.printStackTrace();
        }
        System.out.println("t2 状态:"+ t2.getState());
        //此时无法打断。进入blocked状态。
        t2.interrupt();
    }
}

运行结果显示,此时可以中断正在抢锁的线程。而且该线程其实并没有进入BLOCKED状态,而是WATING状态。也就是说JAVA对这种抢锁的方式,其处理方法是有不同的。
运行结果:
interrupt和抢锁2

总结

  • 对于线程中断的处理要交给线程自身。
  • 可以自己定义标志位来处理。
  • 也可以使用interrupt方法来处理,它能适应的场景很多,在线程sleep()、wait(),甚至抢锁时都能中断。

多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。

https://github.com/forestnlp/concurrentlab

如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。

您的支持是对我最大的鼓励。

Java 线程管理详解:如何优雅地终止执行的线程 在上期文章中,我们讨论了Java 中如何加载外部 JAR 文件,详细介绍了通过命令行、IDE以及动态加载的方式来集成外部库。在处理并发编程时,尤其在引入第三方库或复杂计算时,我们经常会使用多线程来提高程序的性能。然而,线程的生命周期管理常常是并发编程中的一大挑战,特别是当我们需要停止正在运行的线程时。在本篇文章中,我们将重点讨论Java 中如何kill掉执行的线程。尽管 Java 提供了丰富的线程管理工具,直接“杀死”线程的做法并不提倡。 阅读详情

相关推荐

java 优雅的结束线程

本文介绍了Java中三种优雅终止线程的方法。1)中断信号机制:通过Thread.interrupt()发送中断信号,线程通过isInterrupted()检查中断状态,在捕获InterruptedException后需重新设置中断标记并释放资源。2)线程池优雅关闭:先调用shutdown()拒绝新任务并等待完成,再使用awaitTermination()设置超时,必要时用shutdownNow()强制关闭。3)自定义标记位:使用volatile修饰的boolean变量作为停止标志,确保多线程可见性,线程通过

FREEDOM 17万+

多线程之终止线程的四种方法

终止线程的四种方法 第一种:正常运行结束 第二种:使用退出标志退出线程 第三种:interrupt()方法退出 第四种:stop方法退出 第一种:正常运行结束 这种就是程序正常执行完run方法或者cell方法然后退出。 第二种:使用退出标志退出线程 一般run方法执行完,线程就会正常结束,但是我们常常有些需求是需要长时间运行的。只有满足某些条件,才能终止这些线程。所以使用一个变量来控制循环,比如最直接方式就是设置一个boolean类型的变量,并且通过设置这个标志为true或者false来控制循环是否退出。

Jarvis.y的博客 6000

java 四种方法优雅地关闭线程

接下来,我们将通过具体的示例来展示如何优雅地关闭线程。跟着我一起,一步步来,你会发现这一切并不难。📋。

java专栏 1316

java多线程之中断(interrupt)问题

摘要在java中,想要让一个线程停下来,有三种办法: (1)采用退出标志,使得run方法执行完之后线程自然终止。 (2)使用stop强行终止线程,但该方法由于安全问题已经被deprecated。 (3)使用中断机制。

kingbaron的博客 2903

[Stanford CS217]CUDA中矩阵乘法GEMM优化的最关键点:向量内积和向量外积分别取行和取列

对于矩阵C中的每一个tile,矩阵A与矩阵B中的tile只需要读取一次,这样就可以达到 O(N) 的计算强度。理想情况下性能应当受限于算术吞吐量,确切来说对于一个大型方阵,即 M=N=K ,矩阵乘法的数学操作复杂度为 O(N3) ,而数据量为 O(N2) ,同时计算强度为 N (这里的计算强度可以被理解为同一个数据被重复利用的次数)。GEMM就是指计算 C=A∗B+C ,其中 A 、B和C都是矩阵,A是 M×K 的矩阵,B是 K×N 的矩阵, C是 M×N 的矩阵。这就是向量内积和向量外积的最大区别。

weixin_55035144的博客 1427

【RViz2】导入urdf模型时报错:Could not load resource xxx,Unable to open file xxx,Error retrieving file xxx

初学ROS2,想要在RViz2中显示urdf模型。其中.urdf文件引用了外部的meshes文件夹。

qq_43557907的博客 9487

Linux多线程的创建和结束

以上程序还是join成功了,因为线程创建成功后,会先执行join(不能在join前sleep(1),若sleep(1)的话,会让出CPU去执行线程,就会先执行detach。3,线程A 收到 线程B 发送的结束请求,默认情况下,会结束执行结束请求,提前结束(谋害)2,线程运行过程中,满足某个条件提前结束,pthread_exit (吊日子过不下去了)线程有运行代码,有栈空间,TCB(线程控制块),线程号等...线程,是一个并发的执行流程,一般称之为轻量级的进程。线程结束后,需要对其进行资源回收。

yys197的博客 2321

别再死记硬背注意力机制了!用Python+PyTorch从零实现一个HAN(Hierarchical Attention Network)

本文详细介绍了如何使用Python和PyTorch从零实现Hierarchical Attention Network(HAN),通过代码级解析帮助读者深入理解层次注意力机制。文章涵盖词级和句子级注意力层的构建、完整HAN架构的实现、训练技巧及注意力可视化方法,特别适合希望掌握高级NLP技术的开发者。

weixin_28744423的博客 163

Java 终止线程的几种方式

一般run()方法执行完,线程就会正常结束,然而,常常有些线程是伺服线程。他们需要长时间的运行,只有在外部某些条件满足的情况下,才能关闭这些线程。我们可以直接使用thread.stop()来强行终止线程,但是stop()方法是很危险的,就想突然关闭电源一样。所谓正常运行结束,就是程序正常运行结束,线程自动结束。

书香水墨 9480

Ansys 19 Mechanical 流体密封分析

start point是一个特殊的概念,在仿真中ansys将从start point开始搜索会发生流体渗漏的点,可以简单理解为从start point开始,向四周逐个节点比较节点上的接触压紧力与流体压力,如果流体压力大于压紧力,则发生渗漏。事实上此处的最后一个参数是指定start point类型的,为1代表这是一个“open”的start point。在通常情况下,如果不人为指定start point,程序会自动寻找指定流体压力面的“边界”,对于二维情况,就是曲线的两个端点,对于三维情况就是曲面的边线。

Dio980的博客 1533

AI Agent的数据安全与隐私保护措施

AI Agent(人工智能代理)作为一种能够感知环境、做出决策并采取行动的智能实体,在现代社会的各个领域中得到了广泛的应用,如智能客服、自动驾驶、金融风险评估等。随着其应用的不断拓展,AI Agent处理和存储的数据量急剧增加,这些数据包含了大量的个人敏感信息和商业机密。因此,确保AI Agent的数据安全与隐私保护变得至关重要。本文的目的在于全面探讨AI Agent的数据安全与隐私保护措施,涵盖从数据的采集、存储、处理到传输的整个生命周期。

AGI×大数据,开启智能时代的认知跃迁;解码AGI,赋能数据驱动的智能革命。 2866

java 多线程编程系列之5----如何优雅的结束线程

b. 打断时间不精准,比如一个阻塞集合,容量为1000的时候希望结束线程不要再添加,但是由于volatile同步线程标志位的时间控制的不是很精准,有可能线程还要继续跑一会儿才会停止。暂停的时候是不会释放当前线程所拥有的锁的,假如你没有继续启动resume的话就会导致死锁,所以这两个方法suspend()和resume()废弃了,最好别轻易使用。所以stop方法废弃了;的结束线程,但你很难精确的控制线程运行了多少次才结束,所以你只要不依赖线程的中间状态来判断释放结束,volatile还是比较可以的;

u010953747的博客 967

基于PINN求解裂纹扩展问题的优化开发

本文提出了一种基于物理信息神经网络(PINN)的裂纹扩展问题求解方法。通过将线弹性断裂力学控制方程和裂纹尖端场特性嵌入神经网络损失函数,PINN有效克服了传统有限元法在处理裂纹扩展时面临的网格重构困难、计算成本高等问题。文章详细介绍了PINN网络架构设计、裂纹尖端奇异场处理、损失函数构造以及优化策略等关键技术。数值实验表明,该方法能准确预测裂纹扩展路径和应力强度因子,且在处理复杂几何和动态扩展问题时展现出显著优势。相比传统方法,PINN具有无网格、并行计算友好等特点,为断裂力学问题提供了新的求解范式。

huanghm88的专栏 153

Java一个线程能否结束另一个永不停止的线程

在Java中停止一个线程有三种办法: 1.正常结束执行; 2.发生异常; 3.被其他线程stop(Java官方不建议) 参考:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html 为什么Thread.stop弃用? 因为它本质上是不安全的。停...

深度Java 5549

RK3399外设驱动之MIPI

RK3399外设驱动之MIPI 文章目录RK3399外设驱动之MIPIMIPI命令解析入口代码mipi显示配置实例rk3399mipi驱动rk mipi驱动注册过程rk32_mipi_dsi.c解析 内核版本 Linux4.4 平台 rk3399 作者 nineyole MIPI命令解析入口代码 MIPI的入口代码如下:kernel\drivers\video\rockchip\screen\lcd_mipi.c static int __init rk_mipi_screen_in

学无止境 2229

基于ZYNQ PL的AD7606数据采集与FFT实时频谱分析:从硬件到创新实现

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKnmpgJm-1750295287594)(https://example.com/fft_plot.png)]本文涉及代码已在Xilinx ZCU102开发板与AD7606评估板上验证通过,采样率支持200KSPS@8通道,FFT计算延迟<1ms。传统MCU+DSP方案难以兼顾速度与功耗,而ZYNQ的PL+PS架构为这一问题提供了完美解决方案。本文实现的ZYNQ PL方案突破了传统处理器的性能瓶颈,通过。

QQ_778132974的博客 339

[多线程] - 如何优雅的停止一个线程

文章目录一、 为什么要停止一个线程 一、 为什么要停止一个线程

xiaoai1994的博客 1102

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

多线程---多线程基础知识

多线程基础知识2. 线程的基本概念2.1 启动线程的5中方法2.2 线程的状态2.2.1 JAVA的6种线程状态1. NEW2. RUNNABLE (RUNNING+READY)3. WAITING4. TIMED_WAITING5. BLOCKEDTERMINATED2.2 线程的打断2.2.1 interrupt的三个方法2.2.2 interrupt和sleep() wait() join()2.2.3 interrupt 是否能中断正在竞争锁的线程synchronized锁Lock锁如何打断锁竞争的

意田天的博客 1万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

一个线程‘优雅‘结束另一个线程

优雅这一说,其实就是在该线程即将结束时,让其做一部分收尾工作,最后再将其结束。 对于多线程情况下,一个线程要结束其他线程 需要保证,结束该线程的时候释放其运行的支援。于是stop()方法就不行,该方法会致使最后线程资源无法释放,可能永远无法释放。 优雅 即是 运用interruted()方法,来实现这一优雅。 那么给大家列个类举个例子 class TwoTerminna { private Thread monitor; public TwoTerminna() {

你的C有点长 1261

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Java猿社区—Java多线程编程实战—第一章、基础知识篇

@(架构之路之Java多线程编程实战) 欢迎关注作者博客 简书传送门 文章目录前言第一章1、 jconsole监控工具1.1、JVM一些参数1.2、启用 jconsole 以监控 Java 进程1.3、远程监控 Java 进程1.4、jconsole 提供的一些有用信息1.4.1、JVM 设定信息是否起作用检查1.4.2、tomcat 线程池、自定义线程池数量情况实时监控1.4.3、内存使用实际消...

阿祥小王子的博客 679

java如何结束线程_JAVA中终止线程的方法

JAVA中终止线程的方法在Java的多线程编程中,java.lang.Thread类型包含了一些列的方法start(), stop(), stop(Throwable) and suspend(), destroy() and resume()。通过这些方法,我们可以对线程进行方便的操作,但是这些方法中,只有start()方法得到了保留。本文是百分网小编搜索整理的关于JAVA中终止线程的方法,供参...

weixin_39843677的博客 2916

多线程08.优雅结束线程的三种方式

1.使用flag public class ThreadCloseGraceful { private static class Worker extends Thread { private volatile boolean start = true; @Override public void run() ...

124

Java多线程

死锁就是两个线程相互等待对方释放对象锁。很简单,简单看名字就知道是装有线程的池子,我们可以把要执行的多线程交给线程池来处理,和连接池的概念一样,通过维护一定数量的线程池来达到多个线程的复用。

vip_member888的博客 1164

如何优雅的结束一个线程!

this.interrupted(): 测试当前线程是否已经中断;this.isInterrupted(): 测试线程是否已经中断;那么这两个方法有什么图区别呢?我们先来看看this.interrupted()方法的解释:测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程。i

pckzzy119的博客 518

多线程(一)多线程基础

同步和异步;并发编程三要素(原子性、可见性、有序性);Thread的基本使用(interrupted、join、sleep、yield);死锁、活锁与线程饥饿;抢占式调度模型;

HK的博客 3026

java 并发编程八股-多线程篇

Java线程操作在虚拟线程出现前直接使用系统线程,存在性能问题;虚拟线程通过引入中间层优化了性能。多线程编程需关注原子性、可见性和有序性。创建线程可通过继承Thread类、实现Runnable接口或使用线程池,其中线程池能复用线程但需合理配置参数。线程停止方式包括优雅停止(通过标记中断)和暴力停止(已废弃)。线程生命周期包含初始、可运行、阻塞、等待和终止状态。sleep和wait的区别在于锁的释放,notify和notifyAll的区别在于唤醒线程数量。线程间通信可通过volatile、synchroniz

weixin_69759588的博客 702
上一篇: JAVA多线程基础篇 1、线程概念与线程生命周期
下一篇: JAVA多线程基础篇 2、如何创建线程
悟空学编程
博客等级 码龄12年 32粉丝 123原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟空学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值