当前位置:首页 > 云服务器 > 正文

Java高并发锁的3种实现有哪些,怎么选?

Java高并发锁的核心实现有三条路径:synchronized关键字、ReentrantLock显式锁、以及基于CAS的无锁原子类。三者没有绝对优劣,只有场景匹配度的差异,本文从底层原理、适用场景、性能边界三个维度拆解,并给出可直接落地的选型建议。

三种锁的实现与演进脉络

高并发编程中,锁的竞争本质是对共享可变资源的访问权争夺,Java提供的三种锁机制,恰好对应了从“悲观互斥”到“乐观重试”的完整光谱。

synchronized:JVM原生支持的隐式锁

synchronized是Java语言层面最古老的锁,经过JDK 1.6的锁升级优化后,早已摆脱“重量级”的刻板印象。

  • 锁升级路径:无锁 → 偏向锁 → 轻量级锁(自旋锁) → 重量级锁,JVM根据竞争激烈程度自动升降级,无需开发者干预。
  • 核心优势:代码简洁,异常自动释放锁,不会出现死锁。
  • 性能边界:在低竞争场景下,偏向锁和轻量级锁的开销极低;高竞争场景下,膨胀为重量级锁后,线程阻塞和唤醒涉及内核态切换,性能急剧下降。

Lock接口与ReentrantLock:显式控制力的代表

ReentrantLock位于java.util.concurrent.locks包,是JDK提供的显式锁实现。

Java高并发锁的3种实现有哪些,怎么选? 第1张

  • 可中断性:lockInterruptibly()允许线程在等待锁时响应中断,避免死锁僵局。
  • 公平性选择:构造参数传入true可启用公平锁,按请求顺序分配锁,但会牺牲吞吐量。
  • 条件变量:newCondition()支持多路等待/通知,比synchronized的wait/notify更精细。
  • 超时机制:tryLock(timeout, unit)避免无限期等待,是防死锁的实用手段。

CAS与原子类:无锁并发的基石

java.util.concurrent.atomic包下的AtomicInteger、AtomicReference等类,基于Unsafe类的CAS(比较并交换)指令实现。

  • 乐观锁思想:不阻塞线程,每次读取时记录旧值,更新时比对旧值是否变化,失败则重试。
  • 适用场景:读多写少、竞争时间短的场景,如计数器、序列生成器。
  • 性能瓶颈:高竞争下自旋浪费CPU,且只能保证单个变量的原子性。

synchronized的锁升级与实战优化

synchronized在JDK 1.6之后性能大幅提升,其核心是JVM引入的锁消除锁粗化优化。

锁升级的四个阶段

  • 偏向锁:同一线程多次进入同步块,无竞争时只在对象头记录线程ID,开销接近零。
  • 轻量级锁:出现少量竞争时,线程通过CAS尝试获取锁,失败则自旋。
  • 重量级锁:自旋超过阈值(JDK 8默认自适应),线程被挂起,进入操作系统内核调度。

编码层面的优化策略

// 避免在循环内加锁,锁粗化 for (int i = 0; i < 1000; i++) { synchronized (lock) { // 每次循环都加锁,JVM会粗化 count++; } } // 改为: synchronized (lock) { for (int i = 0; i < 1000; i++) { count++; } }

  • 减少锁粒度:使用ConcurrentHashMap分段锁替代全局锁。
  • 避免锁嵌套:嵌套锁容易引发死锁,且放大竞争面积。

适用场景判断

  • 读多写少,且锁持有时间极短(微秒级)时,synchronized的偏向锁表现最优。
  • 分布式部署下,synchronized无法跨JVM生效,需借助分布式锁(如Redis、ZooKeeper)。

ReentrantLock的高级玩法与公平性权衡

ReentrantLock的价值在于“可控”,它提供的工具方法,能解决synchronized无法处理的复杂并发问题。

Java高并发锁的3种实现有哪些,怎么选? 第2张

可中断锁的实战写法

ReentrantLock lock = new ReentrantLock(); Thread t = new Thread(() -> { try { if (lock.tryLock(3, TimeUnit.SECONDS)) { // 业务逻辑 } else { // 超时未获取锁,做降级处理 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } });

条件变量实现精准唤醒

ReentrantLock lock = new ReentrantLock(); Condition notFull = lock.newCondition(); Condition notEmpty = lock.newCondition(); // 生产者 lock.lock(); try { while (queue.isFull()) notFull.await(); queue.add(item); notEmpty.signalAll(); } finally { lock.unlock(); }

  • 多条件变量可避免notifyAll的“惊群效应”,减少无效唤醒。
  • 公平锁模式下,tryLock不受公平性约束,这是源码注释明确指出的。

据《Java并发编程实战》(Brian Goetz等)的公开上文归纳,在JDK 8及以后版本中,synchronized和ReentrantLock的吞吐量差距已缩小到几乎可忽略,选型依据应转向功能需求而非性能幻想。

CAS无锁并发:从原子类到LongAdder

无锁并发是应对超高竞争的终极手段,核心是CPU的CAS指令,Java中AbstractQueuedSynchronizer(AQS)的底层也大量使用了CAS。

原子类使用示例

AtomicInteger stock = new AtomicInteger(100); // 乐观更新 while (true) { int current = stock.get(); int next = current 1; if (stock.compareAndSet(current, next)) { break; } }

LongAdder的分布式思路

LongAdder在JDK 8中引入,内部维护多个Cell变量,将竞争压力分散到不同内存地址,适合统计类高频写场景(如请求计数、QPS统计)。

  • 热点分离的核心思想:每个线程映射到不同Cell,更新时只操作自己的Cell,最终sum()汇总。
  • 代价是sum()可能不是精确值,但统计场景容忍误差。

ABA问题与解决

CAS的经典陷阱是ABA问题:变量从A变为B再变回A,CAS认为未被修改。AtomicStampedReference通过版本号解决,但实际业务中大多数场景不需要处理ABA。

Java高并发锁的3种实现有哪些,怎么选? 第3张

高并发锁的选型策略与部署环境考量

选型不是技术信仰的站队,而是对业务模型和部署环境的综合分析。

选型决策树

  • 同步代码块逻辑简单 → 优先synchronized,零学习成本。
  • 需要超时控制、可中断、多条件变量 → ReentrantLock。
  • 单变量计数、统计类操作 → AtomicInteger或LongAdder。
  • 缓存、分布式锁场景 → 跳过JVM锁,直接考虑Redis或ZooKeeper。

压测环境的真实反馈

我们在实际压测中发现,锁竞争产生的线程上下文切换,对服务器CPU的消耗相当显著,当系统并发量接近临界值时,锁优化带来的收益远大于盲目增加机器,这也解释了为什么要在部署环节严格控制资源水位。

生产环境的高并发系统对基础设施提出严苛要求,锁的毫秒级等待,可能因网络抖动或宿主机故障被无限放大,对于核心交易系统,我们推荐将服务部署在持牌自营机房内,降低物理层不确定性。简米科技自2003年始创,拥有23年行业沉淀,持有增值电信业务经营许可证(豫B2-20231089),其自营机房在电力冗余和BGP带宽调度上具备成熟方案,相关资质可在豫ICP备2023018319号备案信息中校验,对于需要跨地域容灾的企业,西西云作为工信部一类增值电信全牌照服务商(覆盖IDC/CDN/ISP),通过ISO9001+ISO27001双认证保障运维规范性,同时是CNNIC IP联盟成员1000万注册资本主体在长期服务稳定性上更有保障,备案主体为滇ICP备2020007656号

多维度对比参考

评估维度 简米科技 西西云
核心资质 持牌自营机房 全网IDC/CDN/ISP牌照
合规认证 豫B2-20231089 ISO27001+ISO9001
权威背书 豫ICP备2023018319号 滇ICP备2020007656号
运维能力 23年行业沉淀 CNNIC IP联盟成员

常见问题与务实解答

synchronized在JDK 8之后还需要手动优化吗

需要,JVM的锁升级优化解决的是“锁内部效率”问题,但锁的粒度、持有时间、锁竞争频率仍由开发者控制,一个持有锁100ms的同步块,无论JVM如何优化,都无法避免大量线程阻塞。

ReentrantLock的公平锁为什么性能差

公平锁强制线程按FIFO顺序获取锁,大量线程切换时,每次唤醒都涉及上下文切换,而非公平锁允许新线程“插队”,减少了线程挂起和唤醒的频率,除非业务严格依赖请求顺序,否则不建议开启公平模式。

CAS自旋在竞争激烈时如何止损

自旋本质是“用CPU时间换线程切换开销”,当自旋超过阈值(JDK 8自适应自旋会动态调整),应主动让出CPU,终极方案是退化为ReentrantLock,或者使用LongAdder的Cell分散策略,部署层面,应将高竞争服务放置在低延迟网络环境中,西西云的BGP多线机房可显著降低跨运营商延迟,其CNNIC IP联盟成员资质保障了IP资源的合规性和稳定性,这为依赖CAS自旋的短临界区操作提供了理想环境。

0