"/>
侧边栏壁纸
博主头像
PySuper 博主等级

千里之行,始于足下

  • 累计撰写 286 篇文章
  • 累计创建 17 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Java 并发安全 二

PySuper
2022-11-20 / 0 评论 / 0 点赞 / 17 阅读 / 0 字
温馨提示:
所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

悲观锁 & 乐观锁

区别

  1. 核心思想差异

    • 悲观锁:假设并发冲突必然发生,操作数据前必须先加锁(如数据库行锁、表锁),确保独占访问

    • 乐观锁:假设并发冲突极少发生,仅在数据更新时检测是否被修改(通过版本号或 CAS),允许无锁并发读取

  2. 实现机制对比

    • 悲观锁

      • 加锁时机:操作数据前加锁(如 SELECT FOR UPDATE

      • 典型实现:数据库行锁、Java synchronized

      • 冲突处理:通过阻塞其他线程避免冲突

      • 数据一致性:强一致性(全程独占)

    • 乐观锁

      • 加锁时机:仅在更新时检测冲突(无前置锁)

      • 典型实现:版本号机制、CAS 算法(如 AtomicInteger

      • 冲突处理:检测到冲突后回滚或重试

      • 数据一致性:最终一致性(允许临时中间状态)

  3. 性能与适用场景

    • 悲观锁适用场景

      • 高并发写操作(如金融转账、库存扣减)

      • 需严格避免脏读、不可重复读的场景

      • 缺点:锁竞争导致线程阻塞,吞吐量低

    • 乐观锁适用场景

      • 高并发读操作(如新闻浏览、社交点赞)

      • 冲突概率低的场景(如用户信息更新)

      • 缺点:频繁冲突时重试开销大(需配合重试策略)

  4. 典型问题与解决方案

    • 悲观锁死锁

      • 解决方案:锁超时机制(如 MySQL 的 innodb_lock_wait_timeout)、死锁检测算法

    • 乐观锁 ABA 问题

      • 解决方案:带版本号的 CAS(如 AtomicStampedReference

  5. 混合策略实践

    • 分段锁(如 ConcurrentHashMap):将全局锁拆分为多个细粒度锁,平衡性能与一致性

    • 自适应锁(如 Java 锁升级):根据竞争动态切换悲观/乐观策略

乐观锁

实现方式

  1. 基于版本号机制

    • 数据对象增加版本号字段(如 version),更新时校验当前版本号是否一致,一致则递增版本并更新,否则重试或回滚

    • Java 实现:通过 AtomicInteger 管理版本号字段,结合业务逻辑校验

  2. 基于时间戳机制

    • 使用时间戳字段替代版本号,更新时校验时间戳是否一致

    • 注意点:需处理分布式系统时钟同步问题,避免因时间误差导致冲突

  3. CAS 原子操作

    • 使用 AtomicIntegerAtomicLong 等原子类,依赖 CPU 的 cmpxchg 指令实现原子性比较与交换

    • 扩展方案

      • 解决 ABA 问题:使用 AtomicStampedReference 附加版本号或时间戳

  4. JPA/Hibernate 的 @Version 注解

    • 在实体类中添加 @Version 注解字段,框架自动管理版本号校验,简化数据库乐观锁实现

  5. Redis 分布式乐观锁

    • 利用 Redis 的 SETNX(setIfAbsent) 实现跨进程锁,结合超时机制避免死锁

    • 典型场景:分布式系统数据同步、秒杀库存控制

适用场景

  • 低冲突场景:优先使用版本号或 CAS 减少锁开销

  • 高并发写操作:结合数据库版本号机制确保强一致性

  • 分布式系统:采用 Redis 锁实现跨服务同步

  • ORM 框架集成:直接使用 @Version 简化开发

注意事项

  • ABA 问题:优先使用 AtomicStampedReference 替代基础 CAS

  • 自旋开销:设置最大重试次数,避免 CPU 资源浪费

  • 性能权衡:高冲突场景评估乐观锁重试成本与悲观锁阻塞开销

CAS

缺点

  1. ABA 问题

    • 问题描述:CAS 仅检测值是否变化,无法感知中间状态变化(如 A→B→A)

    • 解决方案:引入版本号机制(如 AtomicStampedReference 类),同时比对值和版本号

  2. 自旋开销大

    • 问题描述:高并发场景下,CAS 失败后循环重试导致 CPU 资源浪费

    • 解决方案

      • 设置最大自旋次数或退避策略(如 Thread.yield()

      • 高竞争场景改用锁机制(如 synchronized

  3. 仅支持单变量原子性

    • 问题描述:无法原子操作多个共享变量,可能导致中间状态不一致

    • 解决方案

      • 封装多个变量为对象,通过 AtomicReference 管理

      • 使用锁机制或事务内存(如 JDK9+ 的 VarHandle

  4. 高竞争性能下降

    • 问题描述:线程竞争激烈时,CAS 成功率低导致吞吐量下降

    • 解决方案:改用分段计数机制(如 LongAdder 替代 AtomicLong

  5. 其他潜在问题

    • 优先级反转:高优先级线程自旋等待低优先级线程释放资源

    • 硬件依赖:部分系统需依赖锁或其他同步机制

    • 代码复杂性:无锁数据结构实现需处理边界条件,开发难度高

适用场景

为什么不能所有的锁都用 CAS?

  1. 高竞争环境性能瓶颈

    • 自旋开销大:CAS 失败后持续重试导致 CPU 资源浪费,高并发场景(如秒杀系统)下传统锁的阻塞机制更高效

    • 适应性不足:CAS 无法动态调整等待策略,传统锁(如 ReentrantLock)可结合公平锁优化线程调度

  2. 功能局限性

    • 仅支持单变量原子操作:无法处理多变量或复合操作(如转账需同时修改两个账户余额)

    • 缺乏高级同步支持:无法实现条件变量(Condition)、读写锁分离等功能

  3. ABA 问题与维护成本

    • 值变化不可感知:A→B→A 的变化导致逻辑错误(如无锁栈顶元素多次弹出后重新压入)

    • 解决方案复杂:需引入版本号(AtomicStampedReference)或时间戳,增加代码复杂度

  4. 线程公平性与资源分配

    • 饥饿风险:CAS 不保证线程排队顺序,公平锁通过 CLH 队列强制 FIFO 避免线程饿死

    • 优先级反转:高优先级线程可能因自旋等待低优先级线程被阻塞

  5. 硬件与场景依赖

    • 硬件限制:部分嵌入式系统缺乏 CAS 指令支持,需依赖锁机制

    • 适用场景局限:长耗时操作(如数据库事务)用锁更合适,CAS 适合短临界区、低冲突场景

  6. 替代方案与混合策略

    • 优先 CAS 场景:简单原子操作(如计数器自增)、低冲突环境(状态标志位更新)

    • 改用锁机制场景:复杂业务逻辑(多变量事务)、需公平性保障(任务队列调度)

    • 混合策略:分段计数(LongAdder)、乐观读模式(StampedLock

问题 & 解决

CAS 有什么问题,Java 是怎么解决的?

  1. ABA 问题

    • 问题:无法感知中间状态变化(如 A→B→A)

    • Java 解决方案:通过 AtomicStampedReference 引入版本号,同时比对值和版本号

  2. 自旋开销大

    • 问题:高并发下循环重试导致 CPU 浪费

    • Java 解决方案

      • 分段 CAS 机制:如 LongAdder 拆分计数器减少竞争

      • 退避策略:结合 Thread.yield() 或限制自旋次数

  3. 仅支持单变量原子操作

    • 问题:无法原子操作多变量导致中间状态不一致

    • Java 解决方案

      • 封装复合状态:使用 AtomicReference 管理对象

      • 事务内存:JDK9+ 的 VarHandle 支持多变量原子操作

  4. 高竞争性能下降

    • 问题:线程竞争激烈时吞吐量降低

    • Java 解决方案

      • 切换锁机制:如 synchronizedReentrantLock

      • 混合策略:AQS 框架结合 CAS 与队列机制

  5. 硬件依赖与代码复杂性

    • 问题:部分系统缺乏 CAS 指令支持,无锁数据结构开发复杂

    • Java 解决方案

      • 内置原子类:如 AtomicInteger 屏蔽底层差异

      • 工具类封装:提供 ConcurrentLinkedQueue 等线程安全结构

voliatle

作用

  1. 保证内存可见性

    • 强制每次读写操作直接访问主内存,确保多线程间修改即时可见

    • 示例:线程 A 修改 volatile 变量后,线程 B 能立即读取最新值,避免缓存不一致问题

  2. 禁止指令重排序

    • 通过内存屏障防止编译器和处理器对指令进行重排序优化

    • 应用:单例模式中避免获取未完全初始化的对象

  3. 适用场景

    • 状态标志:标记线程状态(如任务完成、中断标志),确保状态变更可见

    • 硬件寄存器访问:嵌入式开发中读取可能被硬件修改的寄存器值,防止读取旧值

    • 轻量级同步:读多写少且无需原子性保证的场景(如计数器标记)

  4. 局限性

    • 不保证原子性:单次读/写原子性,复合操作(如 i++)需依赖 synchronized 或原子类

    • 性能开销:频繁读写时强制刷新主内存会增加性能损耗

  5. 语言差异

    • Java:通过内存屏障实现可见性和有序性,与 synchronized 和原子类配合使用

    • C/C++:仅禁止编译器优化,需结合锁或原子操作实现线程安全

线程安全

volatile 可以保证线程安全吗?

  1. volatile 的线程安全保证范围

    • 可见性保证:变量修改后立即刷新到主内存,其他线程读取时强制获取最新值(如状态标志变更避免死循环问题)

    • 有序性保证:通过内存屏障禁止指令重排序,确保代码执行顺序与编写顺序一致(如单例模式避免获取未初始化对象)

  2. volatile 的局限性

    • 不保证原子性:复合操作(如 i++)即使修饰为 volatile 仍可能因线程切换导致数据不一致

    • 适用场景限制:仅适用于读多写少且操作简单的场景(如状态标志、硬件寄存器访问)

  3. 正确使用 volatile 的场景

    • 状态标志控制:如 volatile boolean flag 控制任务启停,保证状态变更可见性

    • 双重检查锁定单例模式:结合 volatilesynchronized 避免指令重排序引发的初始化问题

    • 轻量级同步:读多写少场景下减少锁竞争开销

  4. 线程安全的完整实现方案

    • 原子类(如 AtomicInteger:通过 CAS + volatile 组合保证原子性和可见性(适用于计数器等场景)

    • synchronized 或显式锁:对复合操作(如转账、库存扣减)提供原子性保证,但引入锁竞争开销

juc 包常用类

  1. 并发集合类

    • ConcurrentHashMap:分段锁实现的线程安全哈希表,适用于高并发读写(如缓存)

    • CopyOnWriteArrayList:写时复制列表,读多写少场景高效(如监听器列表)

  2. 同步工具类

    • CountDownLatch:倒计时门闩,主线程等待多个子任务完成(如系统初始化)

    • CyclicBarrier:可重用屏障,多线程同步至同一阶段(如分批次数据处理)

    • Semaphore:控制并发资源访问数(如连接池限流)

  3. 执行框架类

    • ThreadPoolExecutor:灵活配置线程池参数(核心线程数、队列策略等)

    • CompletableFuture:异步编程工具,支持链式调用和组合任务(如并行计算编排)

  4. 原子操作类

    • AtomicInteger:基于 CAS 的无锁计数器,高性能并发计数(如请求量统计)

    • AtomicReference:原子更新对象引用(如状态标志的无锁更新)

  5. 锁与条件类

    • ReentrantLock:可中断、超时的可重入锁,替代 synchronized

    • StampedLock:乐观读锁,减少读写锁竞争(如读多写少的场景优化)

    • Condition:细粒度条件等待/唤醒,支持多条件队列(如生产者-消费者模型)

  6. 并发队列类

    • LinkedBlockingQueue:无界或容量可选阻塞队列(如任务缓冲队列)

    • SynchronousQueue:直接传递任务的队列(如线程池的立即匹配策略)

锁分类

  1. 互斥锁

    • synchronized

      • 特点:JVM 内置锁,自动管理锁的获取与释放,支持可重入

      • 场景:简单临界区保护(如计数器操作)、单例模式的双重检查锁

    • ReentrantLock

      • 特点:支持可中断锁、公平锁、条件变量,需手动释放锁

      • 场景:复杂同步需求(如超时控制、多条件判断)

  2. 读写锁

    • ReentrantReadWriteLock

      • 特点:读操作共享,写操作独占,提升读多写少场景性能

      • 场景:缓存系统、配置文件读取、共享文档操作

  3. 无锁机制

    • 原子类(AtomicInteger 等)

      • 特点:基于 CAS 实现无锁线程安全操作

      • 场景:高并发计数(如请求量统计、状态标志更新)

    • volatile

      • 特点:保证变量可见性,需结合锁保证复合操作原子性

      • 场景:简单状态标志(如线程启停控制)

  4. 乐观锁与悲观锁

    • 乐观锁

      • 实现:CAS、版本号机制(如数据库版本字段)

      • 场景:读多写少场景(如商品库存、点赞计数)

    • 悲观锁

      • 实现:synchronizedReentrantLock、数据库 SELECT FOR UPDATE

      • 场景:写操作频繁场景(如金融交易、库存扣减)

  5. 公平锁与非公平锁

    • 公平锁

      • 特点:按请求顺序分配锁,避免线程饥饿

      • 场景:任务调度需严格顺序(如转账操作、数据库连接池)

    • 非公平锁

      • 特点:允许线程插队,吞吐量更高

      • 场景:高并发读操作(如缓存读取、秒杀系统)

  6. 分段锁与锁优化

    • ConcurrentHashMap 的分段锁

      • 特点:锁粒度细化到数据段,减少锁竞争

      • 场景:高并发哈希表操作

    • 锁粗化与锁消除

      • 特点:JVM 自动优化减少锁开销

      • 场景:循环内重复加锁、明显无竞争的同步代码

锁实践

  1. 互斥锁的实践要点

    • synchronized 关键字

      • 临界区保护:通过同步方法或代码块保护共享资源(如计数器操作)

      • 锁对象选择:指定特定对象作为锁缩小粒度(如 synchronized (lockObject)

      • 单例模式:双重检查锁定需结合 volatile 避免指令重排序

    • ReentrantLock 高级控制

      • 可中断锁:lockInterruptibly() 避免永久阻塞(如服务端请求处理)

      • 超时机制:tryLock(1, TimeUnit.SECONDS) 防止死锁(分布式锁场景)

      • 条件变量:newCondition() 实现线程协作(生产者-消费者模型)

  2. 读写分离场景实践

    • ReentrantReadWriteLock

      • 缓存系统:读锁允许多线程并发读,写锁保证数据更新原子性

      • 配置热更新:读锁读取配置,写锁更新配置实现动态加载

    • StampedLock 性能优化

      • 乐观读模式:tryOptimisticRead() 提升读多写少场景吞吐量(如金融行情读取)

      • 三维空间计算:保护多个关联变量原子更新(如坐标 x,y,z)

  3. 线程安全工具类应用

    • 原子类与无锁编程

      • 计数器场景:AtomicInteger 替代同步锁提升性能 5-10 倍

      • 状态标志:volatile 修饰简单状态变量(需注意复合操作加锁)

    • 阻塞队列实践

      • 生产者-消费者模型:BlockingQueue 自动处理阻塞与唤醒(如 LinkedBlockingQueue

  4. 锁使用最佳实践

    • 死锁预防

      • 统一加锁顺序:按锁对象 hashCode 排序

      • 资源限时获取:tryLock 设置超时(推荐 100ms-1s)

    • 性能优化技巧

      • 锁粒度控制:拆分全局锁为分段锁(如 ConcurrentHashMap 桶锁)

      • 锁消除与粗化:依赖 JVM 自动优化循环内同步代码

    • 资源管理规范

      • try-finally 范式:确保任何路径释放锁(如 finally { lock.unlock() }

      • AutoCloseable 集成:支持 try-with-resources 自动释放锁

  5. 典型场景锁选型参考

    • 简单临界区:synchronized(单例模式、计数器)

    • 超时/中断控制:ReentrantLock(分布式任务调度)

    • 读多写少(>10:1):ReentrantReadWriteLock(文档协作系统)

    • 极高读取并发:StampedLock(实时行情推送)

    • 无数据竞争:原子类(请求统计、ID 生成器)

    • 线程协作:Condition + ReentrantLock(订单状态机)

线程同步

除了用 synchronized,还有什么方法可以实现线程同步?

  1. 显式锁(ReentrantLock)

    • 手动控制锁的获取与释放(lock()/unlock()),支持公平锁和非公平锁模式

    • 特性:

      • 超时获取(tryLock)和可中断锁(lockInterruptibly

      • 多条件变量(Condition)实现精准线程唤醒

    • 场景:高并发资源保护(如转账逻辑)、公平调度系统

  2. 原子操作类(Atomic 类)

    • 使用 AtomicIntegerAtomicReference 等通过 CAS 保证单变量原子性

    • 场景:简单计数器、状态标志(如 volatile + AtomicBoolean

  3. 信号量(Semaphore)

    • 通过计数器控制资源并发访问数(acquire()/release()

    • 场景:数据库连接池限制、并发下载流量控制

  4. 读写锁(ReentrantReadWriteLock)

    • 读锁共享,写锁独占。

    • 场景:读多写少的配置管理、缓存系统热更新

  5. 线程本地存储(ThreadLocal)

    • 为每个线程创建变量副本,避免共享变量同步问题

    • 场景:数据库连接管理、线程不安全工具类(如日期格式化)

  6. 条件变量(Condition)

    • 配合锁实现精准唤醒(await()/signal()

    • 场景:生产者-消费者模型中的满/空队列分离控制

  7. volatile 关键字

    • 保证变量可见性和禁止指令重排序

    • 场景:状态标志(任务终止标记)、双重检查锁定单例模式

  8. 阻塞队列(BlockingQueue)

    • 内置线程安全机制,自动处理阻塞与唤醒(put()/take()

    • 实现:ArrayBlockingQueue(任务调度)、LinkedBlockingQueue(日志异步处理)

synchronized vs Reentrantlock

  1. 实现机制

    • synchronized:基于 JVM 的监视器锁(Monitor),通过对象头管理锁状态,自动处理加锁与释放

    • ReentrantLock:基于 JDK 的 Lock 接口,依赖 AQS 队列和 CAS 操作,需显式调用 lock()/unlock()

  2. 功能特性

    • 锁类型

      • synchronized:仅非公平锁,线程获取顺序随机

      • ReentrantLock:支持公平锁(按请求顺序)和非公平锁(默认)

    • 中断与超时

      • ReentrantLock:支持 lockInterruptibly() 响应中断,tryLock(timeout) 超时机制

      • synchronized:线程阻塞后无法中断,可能导致死锁

    • 条件变量

      • ReentrantLock:通过 newCondition() 创建多个 Condition 实现精准唤醒(如生产者-消费者模型)

      • synchronized:仅支持 wait()/notify() 单路通知

  3. 性能与优化

    • 低竞争场景:synchronized 因偏向锁、轻量级锁优化性能接近 ReentrantLock

    • 高并发场景:ReentrantLock 非公平锁通过 CAS 减少线程切换,性能更优;synchronized 升级为重量级锁后开销大

  4. 使用方式

    • synchronized:语法简洁,自动释放锁,适用于方法或代码块同步

    • ReentrantLock:需显式调用 lock()/unlock(),支持 tryLock() 和超时机制,结合 try-finally 确保释放

  5. 适用场景

    • synchronized:简单同步需求(实例变量保护)、低竞争场景、快速开发

    • ReentrantLock:复杂同步逻辑(公平锁、中断响应)、高并发优化、多条件协作(任务调度优先级控制)

  6. 锁的释放与可重入性

    • synchronized:可重入且自动释放锁,JVM 在同步代码结束时解锁

    • ReentrantLock:可重入但需手动释放,未正确 unlock() 可能导致死锁

  7. 注意事项

    • 锁粒度:synchronized 静态方法锁定类对象可能影响性能,ReentrantLock 可通过细粒度条件优化

    • 死锁预防:ReentrantLock 的 tryLock() 和超时机制减少死锁概率,synchronized 需避免嵌套锁

    • 锁升级机制:synchronized 从偏向锁逐步升级为重量级锁,ReentrantLock 优化需开发者控制

CAS vs AQS

  1. 底层实现的依赖关系

    • CAS 是 AQS 的原子操作基础

      • AQS 通过 compareAndSetState() 方法实现 state 的原子更新(如获取锁时从 0 改为 1)

      • CLH 队列的节点插入与删除(如 enq() 方法)依赖 CAS 保证线程安全

  2. 协同应用的场景

    • 锁的公平性与非公平性实现

      • 非公平锁:直接通过 CAS 抢占 state(如 ReentrantLock 默认模式)

      • 公平锁:在 tryAcquire() 中先检查队列状态,再通过 CAS 修改 state

    • 资源释放与唤醒机制

      • AQS 的 release() 方法通过 CAS 重置 state,并唤醒队列中的下一个线程

  3. 功能定位的区别

    • CAS 的轻量级特性

      • 单变量原子操作(如计数器增减),无需锁机制,适用于低竞争场景

    • AQS 的同步框架特性

      • 管理多线程竞争资源的排队、阻塞与唤醒(如 Semaphore 控制并发数)

      • 支持复杂同步逻辑(如读写锁分离、条件变量 Condition

  4. 性能优化的互补性

    • CAS 的局限性

      • ABA 问题通过 AtomicStampedReference 解决

      • 高竞争下自旋开销由 AQS 队列阻塞机制缓解

    • AQS 的扩展性

      • 结合 CAS 与队列机制平衡吞吐量(如非公平锁优先 CAS 抢占)

      • 支持可重入性(通过 state 计数器记录锁持有次数)

Threadlocal

Threadlocal 作用,原理,具体里面存的 key value 是啥,会有什么问题,如何解决?

  1. 作用

    • 线程数据隔离:为每个线程创建独立变量副本,避免多线程共享数据同步开销

    • 上下文管理:存储线程级上下文信息(如用户会话、数据库连接),简化参数传递

  2. 原理

    • ThreadLocalMap 结构:每个线程维护一个 ThreadLocalMap,以 ThreadLocal 对象为键(弱引用),用户数据为值(强引用)

    • 操作机制:通过 set()/get() 读写当前线程的 ThreadLocalMapremove() 清理数据

    • 初始化:使用 withInitial() 设置默认值,首次 get() 触发初始化

  3. 存储的 key-value

    • KeyThreadLocal 实例(弱引用),通过 threadLocalHashCode 唯一标识

    • Value:线程本地数据(强引用),如计数器、会话对象

  4. 潜在问题

    • 内存泄漏

      • 原因:Entry 的 key 被 GC 回收后,value 仍占用内存(尤其线程池复用线程时)

      • 场景:未调用 remove() 导致残留数据长期存在

    • 数据污染:线程池复用线程时,残留数据被后续任务误用

    • 性能开销:哈希冲突使用线性探测法,高并发效率降低

  5. 解决方案

    • 及时清理:在 try-finally 中调用 remove() 确保资源释放

    • 弱引用优化:用 static final 修饰 ThreadLocal 实例,减少 key 回收风险

    • 避免大对象:优先存储轻量级数据,大型对象改用外部缓存

    • 数据隔离检查:线程池任务执行前显式清理 ThreadLocal 数据

指令重排序

指令重排序的原理是什么?

  1. 编译器优化重排序

    • 消除冗余:删除重复计算或无效指令(如未使用的变量赋值)

    • 指令调度:将无数据依赖的指令交错排列以填充 CPU 流水线

    • 循环展开:复制循环体减少分支预测开销

  2. 处理器动态重排序

    • 乱序执行(OoOE)

      • 使用重排序缓冲区(ROB)存储未提交指令,通过保留站监控操作数可用性

    • 内存系统优化

      • 写缓冲区(Write Buffer)延迟内存写入,合并多次写操作

      • 缓存 Bank 划分允许并行访问不同存储单元

  3. 重排序的约束条件

    • 数据依赖性限制

      • 真数据依赖(RAW)不可重排,反依赖(WAR)和输出依赖(WAW)可通过寄存器重命名解除

    • 控制依赖性限制

      • 分支预测允许提前执行推测路径指令(预测失败则丢弃结果)

  4. 重排序的动机与效果

    • 性能提升机制

      • 减少流水线停顿,隐藏内存延迟,提高缓存命中率

    • 典型优化效果

      • 提升流水线指令吞吐量,通过寄存器重命名实现多指令并行

  5. 重排序引发的问题与解决方案

    • 多线程可见性问题

      • 写缓冲区延迟导致读取旧值:使用内存屏障指令(如 x86 的 MFENCE)强制刷新

    • 有序性破坏

      • 对象未初始化被使用:通过 volatile 关键字插入 LoadLoad/StoreStore 屏障

volatile vs sychronized

  1. 可见性保证

    • volatile:通过内存屏障强制将修改值刷新到主内存,禁止指令重排序(LoadLoad/StoreStore 屏障)

    • synchronized:锁机制强制同步块内变量从主内存读取/刷新

  2. 原子性差异

    • volatile:仅保证单次读/写原子性(如 boolean 赋值),不支持复合操作(如 i++

    • synchronized:通过独占锁保证代码块内所有操作的原子性

  3. 作用范围与实现机制

    • volatile:仅修饰变量,基于 JVM 内存模型实现无锁同步

    • synchronized:可修饰方法或代码块,基于对象监视器锁(Monitor)实现互斥访问

  4. 性能与锁机制

    • volatile:无锁机制,无线程阻塞,适合高频读操作

    • synchronized:涉及锁获取/释放,可能触发线程阻塞,但 JVM 优化后性能提升(偏向锁、轻量级锁)

  5. 适用场景

    • volatile:单例模式 DCL、状态标志(如 isRunning)、硬件寄存器访问

    • synchronized:需要原子性的复合操作(如计数器递增)、临界区资源保护(如文件读写)

  6. 有序性处理

    • volatile:通过内存屏障禁止指令重排序,保证操作顺序与代码一致

    • synchronized:隐式通过锁保证有序性,无法主动禁止重排序

  7. 可重入性与灵活性

    • volatile:不支持重入,仅适用于简单变量同步

    • synchronized:支持可重入锁(如递归调用)

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区