04. 就该这么学并发 - 线程的结束

[C++11 并发编程] 04 - 动态选择并发线程的数量 C++标准模板库提供了一个辅助函数 - std::thread::hardware_concurrency(),通过这个函数,我们可以获取应用程序可以真正并发执行的线程数量。下面这个例子,实现了一个并发版本的std::accumulate,它将工作拆分到多个线程中,为了避免过多线程带来的开销,程序指定了每个线程处理数据的最小数量。 头文件和求和操作: #include #include # 阅读详情

前言

接上章“线程的阻塞”,我们继续讲线程的生命周期中最后一个状态“销毁”

销毁(Dead)

线程会以如下3种方式结束,结束后就处于死亡状态:

- run()或call()方法执行完成,线程正常结束

- 线程抛出一个未捕获的Exception或Error

- 直接调用该线程stop()(已废弃,不推荐使用)方法来结束该线程,
  该方法容易导致死锁,通常不推荐使用

线程一旦死亡,就不能复生.

如果在一个死去的线程上调用**start()**方法,会抛出

java.lang.IllegalThreadStateException

stop()被废弃的原因

补充下**stop()**被废弃的原因,

因为它不安全,它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止

假如一个线程正在执行:

synchronized void { 
    x = 3; 
    y = 4;
} 

由于方法是**同步的,多个线程访问时总能保证x,y被同时**赋值,

而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据( 也就是只有x=3被执行了 )

安全的结束线程

线程属于**一次性消耗品,在执行完run()**方法之后线程便会正常结束了,

但有时我们需要线程执行**循环处理任务**,

在这种情况下,一般是将这些任务放在一个**死循环中,如while循环**,

当需要结束线程时,如何退出线程呢?

**Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit**这些终止线程运行的方法已经被废弃了,使用它们是不安全的!想要安全有效的结束一个线程,可以使用下面的方法.

- 正常执行完run方法,然后结束掉;

- 使用退出标识符终止线程 

- 使用interrupt()方法中断当前线程 

正常执行的我们自然不用讲,下面举个例子

使用退出标志终止线程

public class Test {
    public static void main(String[] args) throws Exception {
        Thread stopThread = new Thread(new Runnable(){
            int i=0;  
            @Override
            public void run() {
                while (true) {  
                    if(i==5)  
                        break;  
                    i++;  
                    System.out.println(i);    
                }  
            }
        });
        stopThread.start();
    }
}

使用**标识符来结束一个线程,是一个不错的方法,但其也有弊端**

如果该线程是处于sleep、wait、join的状态时候,while循环就不会执行

那么我们的标识符就无用武之地了,当然也不能再通过它来结束处于这3种状态的线程了.

所以,此时可以使用**interrupt这个巧妙的方式结束掉这个线程.我们先来看看sleep、wait、join**方法的声明:

public final void wait() throws InterruptedException
 
public static native void sleep(long millis) throws InterruptedException

public final void join() throws InterruptedException

可以看到,这三者有一个共同点,

都抛出了一个InterruptedException的异常

在什么时候会产生这样一个异常呢?

每个Thread都有一个中断状状态,默认为false.
可以通过Thread对象的isInterrupted()方法来判断该线程的中断状态.
可以通过Thread对象的interrupt()方法将中断状态设置为true.

当一个线程处于sleep、wait、join这三种状态之一的时候,
如果此时他的中断状态为true,那么它就会抛出一个InterruptedException的异常,并将中断状态重新设置为false.

举个简单的例子验证下以上的特性

class StopThread extends Thread {
    int i = 1;

    @Override
    public void run() {
        while (true) {
            System.out.println("计数器: "+i);
            System.out.println("Interrupted: "+this.isInterrupted());
            try {
                System.out.println("线程将睡眠5s");
                Thread.sleep(5000);
                this.interrupt();
            } catch (InterruptedException e) {
                System.out.println("异常捕获,Interrupted: " + this.isInterrupted());
                return;
            }
            i++;
        }
    }
}
public class Test {
    public static void main(final String[] args) throws Exception {
        StopThread stopThread = new StopThread();
        stopThread.start();
    }
}

结果

计数器: 1
Interrupted: false
线程将睡眠5s
计数器: 2
Interrupted: true
线程将睡眠5s
异常捕获了false

可以看到,在第一次循环中,睡眠**5秒,然后将中断状态设置为true.当进入到第二次循环的时候,中断状态就是第一次设置的true,当它再次进入sleep的时候,马上就抛出了InterruptedException异常,然后被我们捕获了.然后中断状态又被重新自动设置为false**了.

由此,我们可以用**interrupt**来优雅的结束掉线程

class StopThread extends Thread {
    int i = 1;

    @Override
    public void run() {
        while (true) {
            System.out.println("计数器: "+i);
            System.out.println("Interrupted: "+this.isInterrupted());
            try {
                System.out.println("线程将睡眠5s");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("异常捕获,Interrupted: " + this.isInterrupted());
                return;
            }
            i++;
        }
    }
}
public class Test {
    public static void main(final String[] args) throws Exception {
        StopThread stopThread = new StopThread();
        stopThread.start();
        Thread.sleep(2000);
        stopThread.interrupt();
    }
}

执行结果可能有两种

情况1

计数器: 1
Interrupted: false
线程将睡眠5s
异常捕获,Interrupted: false

子线程**start(),主线程睡眠2秒醒来后,调用子线程的interrupt(),此时,子线程正好在sleep(2000)**,所以
循环第一次执行就结束了.

情况2

计数器: 1
Interrupted: false
线程将睡眠5s
计数器: 2
Interrupted: true
线程将睡眠5s
异常捕获,Interrupted: false

子线程**start(),主线程睡眠2秒醒来后,调用子线程的interrupt(),此时子线程还没有处于sleep状态.然后再第2次while循环的时候,再次进入sleep状态,立马抛出InterruptedException**异常

最后,说明下**stop()和interrupt()**的区别

stop()调用后线程直接“强制停止”,不会等到run()方法执行完

interrupt()则会“优雅停止”,等到run()方法执行完再停止

请关注我的订阅号

订阅号.png

并发线程面试题02 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。如果是Runnable的方式提交任务,那工作线程就异常结束,工作线程就没了。这种查询三方服务或者是数据库的操作,可能会因为三方服务的网络抖动,或者查询数据库走没有索引之类的,对阻塞时间有一些影响,此时你会发现,IO密集型的方式,好像没有什么特别好的公式可以直接用。 阅读详情

相关推荐

多线程并发服务器编程

<!-- @page {margin-left:1.25in; margin-right:1.25in; margin-top:0.59in; margin-bottom:0.69in} h1 {margin-top:0.24in; margin-bottom:0.23in; line-height:200%; page-break-inside:avoid}

Skycrab 1万+

【Java 并发】结束任务、中断线程 :blocked、interrupted

文章目录Terminating taskTerminating when blockedBecoming blockedInterruption总结三种 interrupt任务的方法Blocked by a mutexChecking for an interrupt thinking in java 课后答案 Terminating task Terminating when blocked ...

SUKI547的博客 1511

汽车TARA分析实战:从威胁建模到安全需求落地的完整指南

在汽车电子电气系统开发中,威胁分析与风险评估(TARA)是确保功能安全与网络安全的核心方法论。其原理在于通过结构化的流程,系统性地识别资产、分析威胁场景并评估风险,旨在将安全理念前置,从设计源头规避风险。这一过程的技术价值在于将模糊的安全担忧转化为可量化、可追溯的工程需求,从而指导资源精准投放,是符合ISO 26262和ISO/SAE 21434标准要求的关键实践。其应用场景贯穿智能汽车从概念设计到系统开发的各个阶段,尤其对于涉及自动驾驶、智能座舱、车云通信等复杂功能的域控制器开发至关重要。本文聚焦于TAR

weixin_42706667的博客 303

JAVA 等待并发任务结束的几种方式

有时间需要并发执行一系列任务,等待所有的任务结束后再进行一些操作,下面介绍几种实现方式。 假设任务为: Runnable r = () -> { try { Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace();

jackpaul 的博客 3422

SAP数据导入避坑指南:财务期初上线实操全流程(含常见错误排查)

本文详细解析了SAP财务期初数据导入的全流程,包括数据准备、主数据标准化、余额与未清项导入等关键步骤,并提供了常见错误排查和解决方案。特别强调了过渡科目和存货价值的处理技巧,帮助企业在SAP系统上线时避免数据导入陷阱,确保财务数据的准确性和系统平稳运行。

weixin_30832143的博客 395

【Arc GIS 零基础教学】土地利用类型图的绘制方法

​Hi,我是胡学长!今天教大家怎么利用Arc GIS 绘制土地利用类型图。土地利用类型图是表达土地资源的利用现状、地域差异和分类的专题地图。它是研究土地利用的重要工具和基础资料,同时也是土地利用调查研究的主要成果之一。在编制土地利用图的基础上,对当前利用的合理程度和存在的问题、进一步利用的潜力、合理利用的方向和途径,进行综合分析和评价。因此,土地利用图是调整土地利用结构,因地制宜进行农业、工矿业和交通布局、城镇建设、区域规划、国土整治、农业区划等的一项重要科学依据。......

一起来交流学习吧 1万+

Python多线程以及判断线程是否结束

python,多线程,结束

weixin_42991437的博客 7037

笔试题-2023-思特威-数字电路设计(CIS)【纯净题目版】

10 笔试题-2023-思特威-数字电路设计(CIS)【纯净题目版】

w0shishabi的博客 1471

实验三 观察Linux进程线程的异步并发执行

实验三 观察Linux进程线程的异步并发执行 一、实验目的 通过本实验学习如何创建Linux进程及线程,通过实验,观察Linux进程及线程的异步执行。理解进程及线程的区别及特性,进一步理解进程是资源分配单位,线程是独立调度单位。 二、实验环境 硬件环境:计算机一台,局域网环境; 软件环境:Linux Ubuntu操作系统,gcc编译器。 三、实验内容和步骤 进程异步并发执行 1)编写一个C语言程序,该程序首先初始化一个count变量为1,然后使用fork函数创建两个子进程,每个子进程对count加1

weixin_46698891的博客 4334

计算机视觉与深度学习 | 2024年至2025年图像匹配算法总结(原理,公式,代码,开源链接)

以上总结覆盖了当前主流和前沿技术,实际选择需结合具体场景(速度/精度/资源限制)评估。

尘世冰封的专栏 1585

高性能算力中心 — RDMA — Overview

目录 文章目录目录RDMA基于以太网的 RoCE/RDMA 计算优化NVMeNVMe Over Fabrics基于以太网的 NVMe 存储优化 RDMA RDMA(Remote DMA,远程直接内存访问)是一种新的内存访问技术,RDMA 让计算机可以直接存取其他计算机的内存,而不需要经过 CPU 耗时的处理。RDMA 将数据从一个系统快速移动到远程系统存储器中,而不对操作系统造成任何影响。RDMA 技术的原理及其与 TCP/IP 架构的对比如下图所示。 InfiniBand(IB):从一开始就支持 R

烟云的计算 2365

Python多线程使用技巧(同步 + 异步 + 强制结束多线程)

文章目录多线程同步(函数化)多线程同步(使用父类继承方法)多线程异步强制结束多线程 使用方法都写在程序里面的注释中,请尽情享用,如果您觉得不错可以点个赞哦???? 多线程同步(函数化) 代码如下: """ 多线程同步(函数化) """ # -*- coding:utf-8 -*- import threading import datetime import time __author__ = 'Ev...

皮皮鱼哟 3165

【附代码】SSVEP解码算法 - 扩展典型相关分析(eCCA)

本文主要对SSVEP解码算法-eCCA进行复现和测试。本文提供程序的所有代码,只要在Matlab上即可运行。读者可以根据程序自主调整,方便学习以及初学者更好地了解SSVEP的经典算法。

计算机小白 1334

Python并发编程04/多线程

目录 Python并发编程04/多线程 1.生产消费者模型 2.线程的理论知识 2.1什么是线程 2.2线程vs进程 2.3线程的应用 3.开启进程的两种方式 3.1第一种方式 3.2第一种...

weixin_30847939的博客 233

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

04. 异步线程创建-Runnable 方式

1. 核心内容 直接继承Thread类是创建异步线程最简单的方式, 但并不常用. 匿名内部类的方式可以考虑. 1.1 继承Thread 方式特点 开发简单, 可直接通过this.获取线程的相关信息 违背了面向接口编程原则, 需直接继承Thread类, 不利于扩展. 不推荐使用 主线程不能监控子线程何时结束, 也不能获取子线程返回结果 切记启动异步线程的方式是调用star()方法, 而非调用run...

Java 程序源 5211

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【高并发】04 线程安全性、并发安全性、可见性

一、线程安全性 1、当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中,不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类时线程安全的。 2、原子性 提供了互斥访问,同一时刻只能有一个线程来对它进行操作 3、可见性 一个线程对主内存的修改可以及时的被其他线程观察到。 4、有序性 一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序 二、可见性 1、导致共享变量在线程间不可见的原因 线程交叉执行

promiseful的博客 296

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Java高并发编程之第一阶段,多线程基础深入浅出

汪文君高并发编程第一阶段01讲-课程大纲及主要内容介绍 汪文君高并发编程第一阶段02讲-简单介绍什么是线程 汪文君高并发编程第一阶段03讲-创建并启动线程 汪文君高并发编程第一阶段04讲-线程生命周期以及start方法源码剖析 汪文君高并发编程第一阶段05讲-采用多线程方式模拟银行排队叫号 汪文君高并发编程第一阶段06讲-用Runnable接口将线程的逻辑执行单元从控制中抽取出来 汪文君高并发编程第一阶段07讲-策略模式在Thread和Runnable中的应用分析 汪文君高并发编程第一阶段08讲-构造Thr

清心寡欲,做一个专注的程序员 1万+

Spring如何处理线程并发

我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。 我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。但这些资源本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。 虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或

java_fancy的专栏 1万+

并发编程之线程

线程是一个轻量级的进程,是进程中的一个执行单元,是CPU的最小调度单元,一个进程中可以有N个线程。

still_five_Days的博客 1095

iOS底层原理系列04-并发编程

iOS提供了多种并发编程和线程安全的机制,从底层的pthread到高级的GCD和NSOperation。性能需求:对于性能要求极高的场景,考虑os_unfair_lock或dispatch_semaphore;对于一般场景,NSLock和串行队列足够。编程范式:如果偏好面向对象的API,选择NSOperation和NSLock系列;如果偏好函数式编程,选择GCD。任务特性:如果需要复杂的任务依赖和取消机制,选择NSOperation;如果是简单的并发任务,GCD更简洁。同步需求。

但行好事,莫问前程;不忘初心,方得始终! 1219

并发编程专题04-线程池原理分析

线程池原理分析(下一篇:并发编程专题-Callable与Future模式) 并发包 (计数器)CountDownLatchCountDownLatch 类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。CountDown...

qq_29668113的博客 239

Java并发编程 - 认识并发编程 之 线程与多线程

Java 并发编程是现代软件开发中非常重要的一个领域,特别是在多核处理器和分布式系统变得越来越普遍的今天。并发编程涉及如何设计程序使其能够同时执行多个任务,从而提高系统的效率和响应能力。在 Java 中,实现并发的主要机制包括线程(Thread)和多线程(Multithreading)。

qq_33240556的博客 987

java 21 多线程

也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起该线程不会释放锁。4.同步方法中执行了线程对象的wait()方法,当前线程暂停,并释当前线程在同步代码块、放锁。线程体决定了线程的行为。

2301_79982602的博客 1996

Java 进阶之旅-day04:多线程学习~开启并发编程之门

在 Java 编程的世界里,多线程是一项强大且重要的技术。它能显著提升程序的性能和响应能力,让程序在同一时间处理多个任务。接下来,让我们深入了解 Java 多线程的相关知识。

m0_71744084的博客 1343

04 Redis 线程模型简介

1. 同步与异步 当我们同步的执行某个任务时,我们需要等到这个任务执行完给我们反馈结果,我们才能去执行其他的任务。 但是异步的执行某个任务时,我们可以不用等这个任务结束就去执行另一项任务。就像常用到的异步刷新操作。通过异步执行的任务执行结束后我们可以通过回调函数获得结果。 我们可以明显的看出同步和异步的差别,那异步操作那么好为什么不都使用异步操作呢?同步操作存在的意义是有些任务必须是按顺序执行的,就像我们去银行取钱,最起码的保证你的银行账户里有钱你才能取,要不然岂不是去抢银行了。 2. 并发与并行

一个天秤座的架构师 2万+
上一篇: 03. 就该这么学并发 - 线程的阻塞
下一篇: 程序员撩妹指南-抖音爆火3D相册
码哥说
博客等级 码龄8年 5645粉丝 172原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

码哥说

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

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

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

打赏作者

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

抵扣说明:

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

余额充值