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

Java并发锁机制:从原理到实战全方位指南

第一部分:并发编程的基石——可见性、有序性、原子性

在深入锁机制之前,我们必须理解为什么需要锁。并发编程的三大特性是解决线程安全问题的核心。

1.1 可见性

  • 定义:一个线程对共享变量的修改,另一个线程能立刻看到。

  • 根源:CPU缓存(L1/L2/L3)的存在。线程操作的是CPU缓存中的副本,而非主内存。

  • 解决方案volatilesynchronizedLock(底层通过内存屏障实现)。

1.2 有序性

  • 定义:编译器、处理器为了优化性能,可能会对指令进行重排序。

  • 问题:在单线程下重排序无影响,但在多线程下可能导致诡异问题(如经典的Double-Checked Locking问题)。

  • 解决方案volatile(禁止指令重排序)、synchronizedLock(保证临界区内串行)。

1.3 原子性

  • 定义:一个或多个操作要么全部执行且不被中断,要么全部不执行。

  • 问题i++看似一行,实际包含“读-改-写”三步。

  • 解决方案synchronizedLockCAS(Compare And Swap)。


第二部分:JMM(Java内存模型)

JMM是Java并发编程的规范,它定义了线程和主内存之间的抽象关系。

  • 主内存:所有变量存储在主内存中(JVM堆内存的一部分)。

  • 工作内存:每个线程拥有自己的工作内存(CPU寄存器和缓存的抽象),线程对变量的操作必须在工作内存中进行,不能直接读写主内存。

Happens-Before 原则:这是判断数据是否存在竞争、线程是否安全的主要依据。

  • 程序次序规则:一个线程内,书写在前的代码先行发生于书写在后的代码。

  • Volatile规则:对一个volatile变量的写操作,先行发生于后面对这个变量的读操作。

  • 锁规则:对一个锁的解锁(unlock),先行发生于随后对这个锁的加锁(lock)。

  • 传递性:A先行于B,B先行于C,则A先行于C。


第三部分:悲观锁与乐观锁——两种世界观

3.1 悲观锁

  • 思想:认为数据被并发修改的概率很高,所以在操作数据之前先加锁,阻塞其他线程。

  • 代表synchronizedReentrantLock

  • 缺点:阻塞、上下文切换开销大。

3.2 乐观锁

  • 思想:认为数据被并发修改的概率较低,所以不加锁,在更新时通过版本号或CAS机制检查数据是否被修改过。

  • 代表AtomicInteger(CAS)、数据库version字段。

  • 实现核心CAS (Compare And Swap)

    • CAS是一个CPU原子指令(如cmpxchg)。

    • 包含三个参数:内存位置V,预期原值A,新值B。

    • 如果V的值等于A,则更新为B,否则失败。

    • ABA问题:如果V的值从A变为B又变回A,CAS会误认为没有变化。解决方案:AtomicStampedReference(增加版本号/时间戳)。


第四部分:synchronized——内置锁的深度剖析

synchronized是JVM内置锁,其优化程度非常高,在锁竞争不激烈的情况下,性能往往优于ReentrantLock

4.1 用法

  1. 修饰实例方法:锁的是当前实例对象this

  2. 修饰静态方法:锁的是当前类的Class对象。

  3. 修饰代码块:锁的是括号里配置的对象。

4.2 底层原理(JVM层面)

  • 同步代码块:通过monitorentermonitorexit指令实现。

  • 同步方法:通过方法上的ACC_SYNCHRONIZED标志隐式实现。

每个对象都有一个对象头(Object Header),其中包含Mark Word(标记字)。Mark Word存储了对象的哈希码、分代年龄以及锁状态信息

4.3 锁升级(JDK 1.6 优化)

为了减少获取锁和释放锁带来的性能消耗,JVM引入了“锁升级”机制,无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁。这是一个不可逆的过程

  1. 无锁:初始状态。

  2. 偏向锁

    • 场景:只有一个线程访问同步块。

    • 原理:在Mark Word中记录线程ID。当该线程再次进入时,无需CAS操作,只需比较线程ID。

    • 撤销:当有另一个线程竞争锁时,偏向锁撤销,升级为轻量级锁(通常在全局安全点执行)。

  3. 轻量级锁

    • 场景:锁竞争不激烈,线程交替执行。

    • 原理:基于CAS。线程在自己的栈帧中创建Lock Record,尝试用CAS将Mark Word指向Lock Record。如果成功,则获取锁;如果失败,则自旋重试。若自旋次数过多(或竞争加剧),升级为重量级锁。

  4. 重量级锁

    • 原理:依赖于操作系统的mutex互斥量。线程阻塞,从用户态切换到内核态,开销巨大。


第五部分:显式锁——Lock接口与AQS

5.1 Lock 接口的优势

相比于synchronizedLock提供了更灵活的锁操作:

  • 可尝试非阻塞获取锁 (tryLock())。

  • 可中断获取锁 (lockInterruptibly())。

  • 超时获取锁 (tryLock(long time, TimeUnit unit))。

  • 支持公平锁。

5.2 AQS (AbstractQueuedSynchronizer)——并发包的基石

AQS是ReentrantLockCountDownLatchSemaphore等类的底层核心。

核心思想
AQS维护了一个双向链表(FIFO队列,称为CLH队列)来管理阻塞的线程,以及一个int state状态变量来表示锁的状态。

  • state:0表示锁未被占用,1表示锁被占用(可重入锁则>1)。

  • 独占模式:只有一个线程能访问,如ReentrantLock

  • 共享模式:多个线程可以同时访问,如SemaphoreCountDownLatch

核心流程

  1. 线程调用lock()

  2. tryAcquire(arg):尝试通过CAS将state从0改为1。

    • 成功:当前线程获取锁,设置独占线程为当前线程。

    • 失败:将当前线程封装成Node节点,加入等待队列尾部。

    • 接着调用acquireQueued:让节点在队列中“自旋”或“阻塞”(park()),等待前驱节点唤醒。

5.3 ReentrantLock(可重入锁)

  • 可重入性:同一个线程可以多次获取同一把锁,每次获取state加1,释放时state减1,直到0才完全释放。这避免了死锁。

  • 公平锁 vs 非公平锁

    • 非公平锁(默认):当锁被释放时,任何线程都有机会抢占锁(tryAcquire),不管队列中是否有等待线程。优点:吞吐量大。缺点:可能导致线程饥饿。

    • 公平锁:严格按照FIFO队列顺序执行,线程先判断队列中是否有等待者,有则排队。优点:防止饥饿。缺点:并发性能低于非公平锁。

5.4 ReentrantReadWriteLock(读写锁)

读写锁允许同一时刻多个读线程同时访问,但写线程独占。

  • 规则:读-读共享,读-写互斥,写-写互斥。

  • 实现原理:将AQS的state按位切割成两部分,高16位表示读锁计数,低16位表示写锁计数。

  • 锁降级:写锁降级为读锁。步骤:获取写锁 -> 获取读锁 -> 释放写锁。此时线程仍然持有读锁,保证了数据的可见性(防止其他写线程修改)。

5.5 StampedLock(邮戳锁,Java 8)

读写锁的改进版,为了解决读锁阻塞写锁导致的“写饥饿”问题。

  • 三种模式

    1. 写锁:独占。

    2. 悲观读锁:类似ReadWriteLock的读锁。

    3. 乐观读(核心特色):无锁机制。

      • 通过tryOptimisticRead()获取一个邮戳(stamp)。

      • 读取数据。

      • validate(stamp)检查在读取过程中是否有写操作发生。

      • 如果有写操作,则升级为悲观读锁重读,保证一致性。

  • 特点:不可重入,性能极高(尤其在读多写少的场景)。


第六部分:进阶并发工具与锁的关系

6.1 Condition 条件队列

Lock接口通过newCondition()方法获取Condition对象,替代了synchronizedwait/notify机制。

  • 优点:支持多组等待队列(多个Condition对象),实现更精细的控制。

  • 核心方法

    • await():当前线程释放锁并进入等待队列。

    • signal():唤醒等待队列中的一个线程。

6.2 CountDownLatch(倒计数器)

  • 场景:一个线程等待多个线程执行完毕。

  • 原理:AQS共享模式。初始化state = N,每个线程调用countDown()state减1,调用await()的线程在state不为0时阻塞。

6.3 CyclicBarrier(循环栅栏)

  • 场景:多个线程相互等待,直到都到达屏障点。

  • 区别:与CountDownLatch不同,它可以重复使用。

6.4 Semaphore(信号量)

  • 场景:控制并发线程数(限流)。

  • 原理:AQS共享模式。state表示许可证数量,acquire()减,release()加。


第七部分:锁的性能优化与避坑指南

7.1 减少锁的持有时间

  • 坏例子:在synchronized代码块中做耗时IO操作、调用外部接口。

  • 好例子:只在操作共享变量的代码行加锁。

7.2 减小锁粒度

  • 典型应用ConcurrentHashMap。JDK 1.7 使用分段锁(Segment),JDK 1.8 使用CAS +synchronized锁桶的头节点,极大地降低了锁冲突。

7.3 锁粗化

  • JVM会优化:如果反复对同一个对象进行加锁解锁(如循环内加锁),JVM会将锁扩展到循环外部,减少锁操作次数。

7.4 死锁

  • 必要条件:互斥、请求与保持、不可剥夺、循环等待。

  • 排查jstack查看线程堆栈,定位死锁。

  • 解决方案

    1. 保证锁顺序一致(破坏循环等待)。

    2. 使用tryLock超时机制。

7.5 活锁

  • 现象:线程没有阻塞,但一直在重试(如CAS重试),始终无法执行。

  • 解决:引入随机退避机制。


第八部分:实战场景与锁选型

场景推荐方案理由
单机高并发计数AtomicLong/LongAdderLongAdder通过热点分离(Cell数组)减少CAS冲突,性能优于AtomicLong
单机简单同步synchronized代码简洁,JVM内置优化(偏向锁/轻量级锁),无需手动释放锁。
需要公平锁/尝试锁ReentrantLock功能丰富,支持中断、超时、公平性设置。
读多写少(缓存)StampedLock乐观读无锁,性能极致。若JDK8以下,用ReentrantReadWriteLock
控制并发数Semaphore如数据库连接池限制、接口限流。
分布式场景Redisson (Redis) / Zookeeper本地锁在分布式环境中无效,必须使用分布式锁。

第九部分:分布式锁简述(扩展)

虽然本文侧重JVM本地锁,但在微服务架构下,分布式锁是必须了解的延伸。

9.1 基于Redis的分布式锁(Redisson)

  • 核心SET key value NX EX seconds(原子性)。

  • 问题:锁续期(看门狗机制)、Redis主从切换导致锁丢失(红锁Redlock,但有争议)。

9.2 基于Zookeeper的分布式锁

  • 原理:利用临时顺序节点,避免羊群效应。

  • 优点:强一致性(CP),无需续期(会话失效自动释放锁)。

  • 缺点:性能略低于Redis。


第十部分:总结——锁的本质

回顾整个Java锁机制,其演进方向始终是:

  1. 从悲观到乐观:CAS减少了内核态切换的开销。

  2. 从阻塞到自旋:自旋锁减少了线程上下文切换的损耗,但消耗CPU。

  3. 从粗粒度到细粒度:锁分离、分段锁、读写分离,最大化并发度。

  4. 从硬件到软件:利用CPU的CAS指令,配合JMM的内存屏障,在保证安全性的前提下追求极致性能。

核心原则

  • 锁并非越复杂越好。如果只有少量并发,synchronized依然是最优选择。

  • 理解AQS是读懂JUC(java.util.concurrent)包源码的钥匙。

  • 在编写并发代码时,始终牢记Happens-Before规则,不要让思维停留在代码书写的物理顺序上。

http://www.jsqmd.com/news/567329/

相关文章:

  • JVM面试题——方法区、栈、堆
  • ROS2性能优化:深入剖析Fast DDS共享内存传输机制与实践
  • 构建企业级自动化:OpenRPA架构实践与实施指南
  • 机器学习基础(七):激活函数
  • win10深度清理c盘工具推荐:从更新缓存到微信专清
  • 嵌入式系统中的轻量级二维码生成方案
  • Apache SeaTunnel Catalog 功能设计为何能大大简化用户启用步骤?
  • 告别ViT的笨重:手把手教你用SegFormer在Cityscapes数据集上实现高效语义分割
  • 忍者像素绘卷部署教程(含安全加固):非root运行+网络策略+日志审计
  • 《集成第三方工具实现企微防折叠:从API对接、脚本配置到效果监控的全链路实战》
  • KLOGG架构深度解析:基于Hyperscan的高性能日志分析引擎实现原理
  • 深入Linux 0.11内存管理:从/bin/sh启动看零页(Zero Page)的分配与使用
  • XCOM 2模组管理新范式:Alternative Mod Launcher全面解析
  • Matlab数字图像处理核心项目实践:包含直方图均衡、空间过滤器增强、傅立叶变换与频域滤波、噪...
  • 2026年3月导热矽胶片厂家推荐:超高导热矽胶片、矽胶帽套实力测评,非标定制快速交付与热管理解决方案之选 - 品牌企业推荐师(官方)
  • 基于SDMatte的创意设计应用:智能海报与宣传物料生成
  • Qwen3.5-9B-AWQ-4bit多模态落地:制造业设备铭牌识别→型号查询→维保文档匹配
  • 告别技术门槛:用Sakura启动器实现AI工具高效管理的7个实用技巧
  • CRaxsRat v7.4 远程管理工具保姆级安装教程(含虚拟机测试指南)
  • 别再让vCenter单点故障坑了你!手把手教你配置vSphere 6.7的vCenter HA(附网络隔离与存储规划避坑点)
  • 计算机毕业设计springboot房屋租赁管理系统 基于SpringBoot的在线房产租赁服务平台设计与实现 SpringBoot框架下的智能化住房租赁信息管理系统
  • 手把手教你用Coze搭个‘论文小助理’:自动摘要、分类,还能给同组同学发Telegram周报
  • k8s网络Cilium4 - 小镇
  • TMSpeech:开源本地语音转文字工具的隐私革命
  • 3步解锁小米手表表盘设计:Mi-Create零基础定制专属智能穿戴界面完全指南
  • AI 赋能热电数智升级,蓝卓智慧电厂方案亮相第四届热电技术交流会
  • 腾讯地图AI驱动的时空智能开放平台技术解析
  • Windows7部署WordPress傻瓜式教程(IIS7.5+MySQL+PHP+WordPress)
  • AI大模型产品经理成长之路:从零基础到专家的详细学习路线全解析【AI大模型产品经理学习路线】
  • 2026年3月电脑花样机厂家推荐,花样机平缝机内衣机开袋机,非标定制快速交付实力源头厂商 - 品牌企业推荐师(官方)