当前位置: 首页 > backend >正文

线程安全 — 场景、解决、悲观锁、乐观锁

线程安全 — 场景、解决、悲观锁、乐观锁

线程不安全的情景

  • 多个线程**同时复合操作(先读取后修改,先检查后执行)**共享对象(静态对象或单例全局对象)。
  • 对于局部对象,只有在其作为返回值引用静态变量创建子线程去修改时才可能会导致线程不安全,关键是看该对象有没有被多个线程同时复合操作的可能性。
  • 总结:所以线程不安全的关键一是看要修改的对象是否为所有线程的共享对象,二是有没有进行复合操作。开发中最常见的例子是定义一个静态的HashMap集合来维护某种映射关系,此时需要格外注意。

解决线程不安全

  • 使用volatile修饰共享变量(可见性)和采用线程安全的集合,比如ConcurrentHashMapCopyOnWriteArrayList等等。

    这里有个误区,以为使用了线程安全的集合就一定能保证线程安全。

    ConcurrentHashMap为例子,ConcurrentHashMap底层通过CASsynchronized来保证节点添加/删除时的线程安全性。

    但如果进行复合操作,比如说先读取键值对是否存在修改键值对仍有线程安全问题,因为这两个操作并不是原子的,可能线程A发现键值对存在后,CPU执行权就被线程B抢占,而线程B又把该键值对删除,从而导致线程不安全。因此ConcurrentHashMap提供了一些原子方法,比如putIfAbsent()conputeIfAbsent()

  • 悲观锁:使用synchronized或 AQS中的Lock锁来保证共享资源被线程独占。其中可以使用**读写锁(ReentrantReadWriteLock)**有效提高性能。

    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); // 获取读锁,用于读操作
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); // 获取写锁,用于写操作
    

AQS与Synchronized的区别

特性 / 维度synchronizedAQS(AbstractQueuedSynchronizer)
类型关键字类库
底层实现基于对象的 Monitor基于 FIFO 队列 + CAS + volatile
锁模式独占锁,非公平可实现独占锁、共享锁、公平/非公平锁等
可中断性不支持线程中断支持中断(acquireInterruptibly()),即中断正在等待锁的线程
是否支持条件变量通过WaitSet实现(Object.wait/notify支持多个条件变量来实现等待、唤醒机制(Condition.await/signal
是否可重入是(可重入锁)取决于具体实现类(如 ReentrantLock 支持)
适合场景在竞争激烈情况下为重量级锁,性能较差,在竞争少时为轻量级锁,性能较好在竞争激烈情况下,更灵活,有多种解决方案,性能更好
  • 乐观锁:

    • 定义:等到真正更新数据的时候才检查在此期间是否有其他线程修改过数据。如果检测到数据已被修改,则更新失败,即CAS。
    • 实现:对数据表添加一个版本号时间戳字段,在执行DML时判断版本号和之前查询的版本号是否相同,如果不同则让前端重试请求
    • 与悲观锁比较:乐观锁虽然避免了悲观锁底层操作系统的用户态内核态的切换阻塞/唤醒线程需要调用操作系统的原语),但性能并不是总是更优异,当线程竞争激烈时,乐观锁会频繁修改失败,导致长时间自旋,所以更适合低并发的场景。
  • 转为不共享设计,比如使用ThreadLocal让每个线程拥有自己的变量,或者在方法中创建一个独立的变量副本(深拷贝),方法中的所有操作都基于该副本。

  • 不可变对象:对象创建后状态不可修改,天然线程安全:private final String serverUrl;

  • 原子类:基于 CAS(Compare-And-Swap)实现的无锁线程安全操作:private AtomicInteger counter = new AtomicInteger(0);

http://www.xdnf.cn/news/10695.html

相关文章:

  • mysql离线安装教程
  • 计算机视觉NeRF
  • 【GESP真题解析】第 6 集 GESP 三级 2023 年 9 月编程题 1:小杨的储蓄
  • 电路图识图基础知识-高、低压供配电系统电气系统的继电自动装置(十三)
  • android binder(三)binder.c函数分析
  • 审计- 1- 审计概述
  • Python-matplotlib中的Pyplot API和面向对象 API
  • UE5 创建2D角色帧动画学习笔记
  • 网络节点排查
  • RAG系统中如何检测幻觉?
  • 【dshow】VIDEOINFOHEADER2 头文件
  • Arch安装megaton
  • PHP7+MySQL5.6 查立得轻量级公交查询系统
  • ck-editor5的研究 (5):优化-页面离开时提醒保存,顺便了解一下 Editor的生命周期 和 6大编辑器类型
  • 【LeetCode 题解】两数之和(C++/Python 双解法):从语法到算法的全面解析
  • #14 学习日志
  • ②Pybullet干涉检查指令getContactPoints与 getClosestPoints介绍
  • Vue-5-基于JavaScript和plotly.js绘制数据分析类图表
  • ubuntu22.04安装megaton
  • 图像任务中的并发处理:线程池、Ray、Celery 和 asyncio 的比较
  • 经典数学教材推荐(AI相关)
  • rabbitmq Fanout交换机简介
  • 二叉查找树 —— 最近公共祖先问题解析(Leetcode 235)
  • 什么是绿电直连
  • ESP32之Linux编译环境搭建流程
  • 电脑wifi显示已禁用怎么点都无法启用
  • 浅谈量子计算:从实验室突破到产业落地的中国实践
  • Java详解LeetCode 热题 100(23):LeetCode 206. 反转链表(Reverse Linked List)详解
  • 使用pdm+uv替换poetry
  • 20250602在荣品的PRO-RK3566开发板的Android13下的uboot启动阶段配置BOOTDELAY为10s