并发编程(5)

抛异常时会释放锁。

当线程在 synchronized 块内部抛出异常时,会自动释放对象锁。

public class ExceptionUnlockDemo {private static final Object lock = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (lock) {System.out.println("线程1获取到锁");try {// 模拟操作Thread.sleep(1000);// 抛出异常throw new RuntimeException("模拟异常");} catch (InterruptedException | RuntimeException e) {System.out.println("线程1捕获异常: " + e.getMessage());}// 异常发生后,锁已经被释放System.out.println("线程1执行完毕");}});Thread t2 = new Thread(() -> {// 短暂等待,确保t1先执行try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2尝试获取锁");synchronized (lock) {System.out.println("线程2获取到锁");}});t1.start();t2.start();}
}

结果:

线程1获取到锁
线程1捕获异常: 模拟异常
线程2尝试获取锁
线程2获取到锁
线程1执行完毕

从输出能够看出,线程 1 在抛出异常之后,线程 2 就能够获取到锁,这表明线程 1 的锁已经因为异常而被释放了。

加锁的区域就是同步代码块

在 Java 中,被锁保护的代码区域被称为同步代码块(Synchronized Block)。当多个线程访问同一把锁的同步代码块时,会强制串行执行,从而保证线程安全。同步的意思就是说比如说一个线程在执行时访问了一个别锁的共享资源,在时间片结束后还占着资源,另一个线程也不能访问

public class SynchronizedExample {private final Object lock = new Object();  // 锁对象// 方法级同步(隐式锁为 this)public synchronized void method1() {// 同步方法的代码}// 代码块级同步(显式指定锁对象)public void method2() {synchronized (lock) {// 同步代码块,同一时刻只能被一个线程执行}}
}

monitor进monitor出

对于我们人来说,一个程序的执行开始和执行结束是可以理解的,但机器不行。我们计算机内部的指令经过编译之后是一长串的代码,程序以数组的形式存到页里,我们加的锁的区域在数组里不允许同时被访问。那么我们需要一些表记指出哪些区域的代码是被锁的不能同时访问。

首先我们要进行范围标记枷锁加锁我们知道这块资源是加锁标记还得。注意我们加锁是这样的。

第一,我们对要操作的资源加上所标记。

第二,我们要操作的资源这块是有这些指令对资源进行操作,这一堆指令要对资源进行操作,其中指令到这些区域。这些区域对它的操作不能是同时进行的,所以我们加锁的话有两个区域,一个是对资源本身加标记,另外一个对操作的资源的指令。也得指定所在的范围同步范围。

 monitor进monitor出就是标记这些指令里那一块是不能同时被线程所操作。

JAVA对象头

snchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,1字宽等于4字节,即32bit.

我们在堆区域创建对象,每一个对象都有锁表记和对象表记,所属类型:类的地址指向方法区。

如果有数组的话有数组长度。

如果一个64位的操作系统可以安装32位或64位的程序。32位的操作系统只可以安装32位程序。如果cpu的一个时钟周期能够运算32位,那他只能安装32位的操作系统。那如果一个程序是64位的程序,它里边一个字宽就是64位。是32位的程序,那么它就只能说一个字宽是32位。从这个推理下,我们在64位的操作系统下安装了32位的程序,那么它一个字就是32位。也就是说字宽最终是受程序的位数影响

 
锁的升级与对比

在 Java 中,锁的升级是 JVM 为了优化锁性能而引入的机制。随着多线程竞争的加剧,锁会从无锁状态逐步升级为偏向锁、轻量级锁,最终升级为重量级锁。这种升级过程是不可逆的,旨在平衡不同竞争程度下的性能开销。

1. 锁升级的流程

锁的状态会根据竞争情况逐步升级:

无锁 → 偏向锁 → 轻量级锁 → 重量级锁

1.1 无锁状态(Normal)
  • 特点:对象头的 Mark Word 存储哈希码、分代年龄等信息。
  • 场景:没有线程访问同步块时。
1.2 偏向锁(Biased Locking)
  • 适用场景:只有一个线程频繁访问同步块。
  • 原理
    • 首次获取锁时,JVM 会在 Mark Word 中记录线程 ID(通过 CAS 操作)。
    • 该线程后续进入同步块时,无需任何同步操作,直接判断 Mark Word 中的线程 ID 是否匹配。
  • 优点无竞争时几乎无额外开销,性能最优。
  • 缺点:当其他线程尝试竞争锁时,需要撤销偏向锁,消耗 CPU。

 

 

1.3 轻量级锁(Lightweight Locking)
  • 适用场景:多个线程交替访问同步块(无竞争)。
  • 原理
    • 线程在栈帧中创建锁记录(Lock Record),并通过 CAS 将 Mark Word 复制到锁记录中。
    • 同时,Mark Word 会指向锁记录的地址。
    • 如果 CAS 成功,线程获得轻量级锁;如果失败,表示有竞争,锁会升级为重量级锁。
  • 优点:竞争不激烈时避免线程阻塞,减少用户态和内核态的切换。不进阻塞队列,快速循环看看自己请求的资源有没有解锁。
  • 缺点:频繁 CAS 操作可能消耗 CPU。
1.4 重量级锁(Heavyweight Locking)
  • 适用场景:多个线程同时竞争锁。
  • 原理
    • 依赖操作系统的互斥量(Mutex),线程会被阻塞并进入等待队列。
    • 锁释放时,需要唤醒等待的线程,涉及用户态和内核态的切换。
  • 缺点:线程阻塞和唤醒的开销大,性能最差。重量解锁的加锁和解锁都挺麻烦,不仅仅是修改对象头标记这么简单,还有很多枷锁过程,枷锁竞争过程。
 四种锁的对比
特性无锁偏向锁轻量级锁重量级锁
适用场景无同步需求单线程访问多线程交替访问多线程同时竞争
实现方式Mark Word 存储哈希码等信息Mark Word 存储线程 IDMark Word 指向线程栈中的锁记录Mark Word 指向 Monitor 对象
线程状态无需阻塞无需阻塞自旋等待阻塞等待
性能开销最低低(首次 CAS)中等(CAS 操作)最高(内核态切换)
优缺点无同步开销无竞争时最优避免线程切换线程频繁阻塞 / 唤醒
释放机制无需释放线程退出同步块时不释放,其他线程竞争时撤销解锁时通过 CAS 恢复 Mark Word解锁时唤醒等待线程

现在是几个线程对资源进行竞争只有一个才能竞争成功!只有一个竞争成功加上了锁。其他就会竞争失败。竞争失败的话会竞争失败会进阻塞队列。为什么进入阻塞队列:成功的线程即使时间片到期了,下次还是选中他可以立马执行。效率最高每个加锁资源对应一个阻塞队列
 

 然后第一个成功占资源的线程已经执行结束后,会给所有在阻塞队列的线程发通知,让他们回到就绪队列。

比较并且交换

比较并交换(Compare-and-Swap, CAS) 是一种实现无锁(Lock-Free)算法的原子操作,用于在多线程环境中实现同步。它允许线程在不使用锁的情况下安全地修改共享数据,从而避免了锁带来的上下文切换和线程阻塞开销。这种方式也是实现了写后读。

  • 如果内存地址 V 中的值等于预期旧值 A,则将该位置的值更新为新值 B,并返回 true
  • 如果内存地址 V 中的值不等于预期旧值 A,则不执行更新,返回 false

但也有缺点,他是分两步的先比较再交换。比如说线程1先比较,发现没问题可以交换,但此时时间片到期。线程2进入也比较发现也没问题,也要交换就有问题了。 他不是原子操作,所以无法实现。只能调用操作系统底层。

流水线技术。他减少了电路的切换。提升的整体速度。但是指令顺序会发生变化,会导致后边有个指令重排序导致的并发问题。

java中哪些类是线程安全的?concurrent 开头的是线程安全的集合。Atomic. 开头的也是线程安全的

private AtomicInteger atomicI = new AtomicInteger(0);private int i = 0;public static void main(String[] args) {final Counter cas = new Counter();List<Thread> ts = new ArrayList<Thread>(600);long start = System.currentTimeMillis();for (int j = 0; j < 100; j++) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {cas.count();cas.safeCount();}}});ts.add(t);}for (Thread t : ts) {t.start();}// 等待所有线程执行完成for (Thread t : ts) {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(cas.i);System.out.println(cas.atomicI.get());System.out.println(System.currentTimeMillis() - start);}/**        * 使用CAS实现线程安全计数器        */private void safeCount() {for (;;) {int i = atomicI.get();boolean suc = atomicI.compareAndSet(i, ++i);if (suc) {break;}}}/*** 非线程安全计数器*/private void count() {i++;}}

一个使用AtomicInteger实现的线程安全计数器atomicI,另一个是普通的非线程安全整数i。在多线程环境下运行这两个计数器,会看到不同的结果。

主要分析

  1. 线程安全问题

    • count()方法使用普通的i++操作,在多线程环境下会出现竞态条件(Race Condition),导致最终结果小于预期值。
    • safeCount()方法使用AtomicInteger和 CAS 操作,保证了原子性,最终结果是正确的。
  2. CAS 操作的实现

    for (;;) {int i = atomicI.get();boolean suc = atomicI.compareAndSet(i, ++i);if (suc) {break;}
    }
    
     

    这段代码通过循环 CAS 操作来保证原子性:

    • 获取当前值
    • 尝试更新为新值
    • 如果失败则重试,直到成功
  3. 性能考虑

    • 原子操作虽然避免了锁的开销,但在高竞争环境下可能会因为频繁重试而降低效率
    • 非原子操作虽然没有这些开销,但结果是错误的,所以不能只考虑性能

输出结果

对于 100 个线程,每个线程执行 10000 次递增操作:

  • atomicI.get()的输出结果应为:100 * 10000 = 1,000,000
  • i的输出结果通常会小于 1,000,000,因为非线程安全操作会导致部分递增丢失
  • 执行时间取决于系统环境,但通常原子操作会比非原子操作慢一些
  •  我们看由于它没有加锁。他100万字肯定有相互覆盖,最终的结果肯定不到100万,就极大概率不到100万,有没有看到100万的一个极小的可能?比较大的可能是到不了100万。因为会有相互覆盖的问题。能理解的加九。对他100万次调用

 ABA问题

在多线程编程中,ABA 问题(ABA Problem)是使用 CAS(Compare-and-Swap) 操作时可能遇到的一种特殊问题。它发生在一个值从 A 变为 B,然后再变回 A 的过程中,而 CAS 操作无法感知这个中间变化,误认为值没有被修改过。

1. ABA 问题的本质

CAS 操作的核心逻辑是:“如果当前值是预期值 A,则将其更新为 B”。但如果在检查期间,值经历了 A → B → A 的变化,CAS 会认为值 “没有变化” 并成功更新,而实际上值已经被其他线程修改过,可能导致潜在的错误。

2. ABA 问题示例

假设有一个共享变量 int value = 10,两个线程 T1 和 T2 同时操作它:

  1. T1 读取 value:T1 准备将 value 从 10 增加到 11,它先读取 value 的当前值为 10。
  2. T2 修改 value:T2 先将 value 改为 20,然后又改回 10(即 10 → 20 → 10)。
  3. T1 执行 CAS:T1 执行 CAS(预期值=10, 新值=11),发现当前值确实是 10,认为没有变化,成功将 value 改为 11。

问题:T1 认为 value 没有被修改过,但实际上它经历了 10 → 20 → 10 的变化,可能导致 T1 的操作基于错误的假设(例如,T1 可能期望 value 自从读取后没有被其他线程动过)。

3. ABA 问题的危害

  • 数据一致性风险:在某些业务逻辑中,中间状态的变化可能影响最终结果。例如:
    • 链表操作:删除节点 A,插入新节点 B(内容与 A 相同),此时 CAS 可能误判节点未变。
    • 账户余额:余额从 100 变为 200 再变回 100,CAS 可能允许重复扣款。
  • 逻辑错误:某些操作依赖值的连续性变化,而 ABA 可能破坏这种假设。

4.解决方法

解决 ABA 就加版本号就可以了

线程可见性

一个线程对共享变量做出了修改,其他线程能及时读取到最新的修改,这叫线程可见性
 

线程间如何通信和进程间如何通信

进程是由线程组成的。进程所有的功能线程全都有。进程所拥有的功能线程全都有。然后。线程拥有的功能进程不一定有进程的通信方式线程全都有

里边通信其实是指交换信息的意思,就把消息传递过去,线程的通信方式有两种共享内存和消息传递
 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/80602.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

贵州某建筑物挡墙自动化监测

1. 项目简介 某建筑物位于贵州省某县城区内&#xff0c;靠近县城主干道&#xff0c;周边配套学校、医院、商贸城。建筑物临近凤凰湖、芙蓉江等水系&#xff0c;主打“湖景生态宜居”。改建筑物总占地面积&#xff1a;约5.3万平方米&#xff1b;总建筑面积&#xff1a;约15万平…

6个月Python学习计划:从入门到AI实战(前端开发者进阶指南)

作者&#xff1a;一名前端开发者的进阶日志 计划时长&#xff1a;6个月 每日学习时间&#xff1a;2小时 覆盖方向&#xff1a;Python基础、爬虫开发、数据分析、后端开发、人工智能、深度学习 &#x1f4cc; 目录 学习目标总览每日时间分配建议第1月&#xff1a;Python基础与编…

【FAQ】HarmonyOS SDK 闭源开放能力 —Vision Kit (3)

1.问题描述&#xff1a; 通过CardRecognition识别身份证拍照拿到的照片地址&#xff0c;使用该方法获取不到图片文件&#xff0c;请问如何解决&#xff1f; 解决方案&#xff1a; //卡证识别实现页&#xff0c;文件名为CardDemoPage&#xff0c;需被引入至入口页 import { …

AI全域智能监控系统重构商业清洁管理范式——从被动响应到主动预防的监控效能革命

一、四维立体监控网络技术架构 1. 人员行为监控 - 融合人脸识别、骨骼追踪与RFID工牌技术&#xff0c;身份识别准确率99.97% - 支持15米超距夜间红外监控&#xff08;精度0.01lux&#xff09; 2. 作业过程监控 - UWB厘米级定位技术&#xff08;误差&#xff1c;0.3米&…

安全强化的Linux

SElinux简介 SELinux是security-Enhanced Linux的缩写,意思是安全强化的linux SELinux主要由美国国家安全局(NSA)开发,当初开发的目的是为了避免资源的误用。传统的访问控制在我们开启权限后,系统进程可以直接访问 当我们对权限设置不严谨时,这种访问方式就是系统的安全漏洞 在…

机器学习第十六讲:K-means → 自动把超市顾客分成不同消费群体

机器学习第十六讲&#xff1a;K-means → 自动把超市顾客分成不同消费群体 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章&#xff1a;DeepSeek R1本地与线上满血版部署&#xff1a;超详细手把手指南 K-me…

spring中yml配置上下文与tomcat等外部容器不一致问题

结论&#xff1a;外部优先级大于内部 在 application.yml 中配置了&#xff1a; server:port: 8080servlet:context-path: /demo这表示你的 Spring Boot 应用的上下文路径&#xff08;context-path&#xff09;是 /demo&#xff0c;即访问你的服务时&#xff0c;URL 必须以 /d…

论文研读——《AnomalyGPT:使用大型视觉语言模型检测工业异常》

这篇论文提出了 AnomalyGPT&#xff0c;一个基于大型视觉语言模型的工业异常检测框架&#xff0c;首次将通用多模态对话能力引入工业视觉场景&#xff0c;通过引入图像解码器增强像素级感知&#xff0c;设计 Prompt 学习器实现任务自适应控制&#xff0c;并利用合成异常样本解决…

供应链安全检测系列技术规范介绍之一|软件成分分析

软件成分分析的概念及意义 软件成分分析Software Compostition Analysis&#xff08;SCA&#xff09;是一种用于管理开源组件应用安全的方法。软件成分分析系统可以快速跟踪和分析应用软件的开源组件&#xff0c;发现相关组件、支持库以及它们之间直接和间接依赖关系&#xff0…

conda更换清华源

1、概览 anaconda更换速度更快、更稳定的下载源&#xff0c;在linux环境测试通过。 2、conda源查看 在修改之前可以查看下现有conda源是什么&#xff0c;查看conda配置信息&#xff0c;如下&#xff1a; cat ~/.condarc 可以看到你的conda源&#xff0c;以我的conda源举例&am…

Docker配置容器开机自启或服务重启后自启

要将一个 Docker 容器设置为开机自启&#xff0c;你可以使用 docker update 命令或配置 Docker 服务来实现。以下是两种常见的方法&#xff1a; 方法 1&#xff1a;使用 docker update 设置容器自动重启 使用 docker update 设置容器为开机自启 你可以使用以下命令&#xff0c…

Flink 的水印机制

Apache Flink 的 水印机制&#xff08;Watermark Mechanism&#xff09; 主要用于解决 事件时间流中的乱序问题&#xff08;Out-of-Order Events&#xff09;&#xff0c;确保窗口&#xff08;Window&#xff09;能够在合适的时间触发计算&#xff0c;从而提供准确、一致的处理…

【每天一个知识点】embedding与representation

“Embedding&#xff08;嵌入&#xff09;”与“Representation&#xff08;表示&#xff09;”在机器学习、自然语言处理&#xff08;NLP&#xff09;、图神经网络等领域常被使用&#xff0c;它们密切相关&#xff0c;但语义上有一定区别。 一、定义 1. Representation&#…

SpringBoot(二)--- SpringBoot基础(http协议、分层解耦)

目录 前言 一、SpringBoot入门 1.入门程序 2.解析 二、HTTP协议 1.HTTP概述 2.HTTP请求协议 2.1 GET方式的请求协议 2.2 POST方式的请求协议 2.3 两者的区别 2.4 获取请求数据 3.HTTP响应协议 三、分层解耦 1.三层架构 2.IOC&DI 2.1 入门 2.2 IOC详解 2.…

Please install it with pip install onnxruntime

无论怎么安装都是 Please install it with pip install onnxruntime 我python 版本是3.11 &#xff0c;我换成3.10 解决了

【数据结构入门训练DAY-35】棋盘问题

本次训练聚焦于使用深度优先搜索&#xff08;DFS&#xff09;算法解决棋盘上的棋子摆放问题。题目要求在一个可能不规则的nn棋盘上摆放k个棋子&#xff0c;且任意两个棋子不能位于同一行或同一列。输入包括棋盘大小n和棋子数k&#xff0c;以及棋盘的形状&#xff08;用#表示可放…

【日常笔记】wps如何将值转换成东西南北等风向汉字

在WPS表格中&#xff0c;若要将数值&#xff08;如角度值&#xff09;转换成“东、南、西、北”等风向汉字&#xff0c;可通过以下步骤结合自定义函数或条件判断实现&#xff1a; 一、wps如何将值转换 方法一&#xff1a;使用LOOKUP函数&#xff08;简化公式&#xff09;&…

Web性能优化的未来:边缘计算、AI与新型渲染架构

一、边缘计算与性能优化深度整合 1.1 边缘节点计算卸载策略 • 智能任务分割:将非关键路径计算卸载到边缘节点 // 客户端代码 const edgeTask = new EdgeTask(image-processing); edgeTask.postMessage(imageData, {transfer

spring中的EnvironmentPostProcessor接口详解

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 EnvironmentPostProcessor 是 Spring Boot 提供的一个关键扩展接口&#xff0c;允许开发者在 Spring 应用环境初始化后、应用上下文创建前&…

Vue3知识点梳理

注&#xff1a;纯手打&#xff0c;如有错误欢迎评论区交流&#xff01; 转载请注明出处&#xff1a;https://blog.csdn.net/testleaf/article/details/148056625 编写此文是为了更好地学习前端知识&#xff0c;如果损害了有关人的利益&#xff0c;请联系删除&#xff01; 本文章…