深挖 JVM 锁膨胀底层:从无锁到重量级锁全链路拆解与高并发调优实战

简介: 本文深入解析JVM中synchronized锁的膨胀机制及优化策略。主要内容包括:1) 对象头结构与MarkWord状态变化,详解无锁、偏向锁、轻量级锁和重量级锁的实现原理;2) 锁膨胀全链路流程,分析不同竞争场景下的锁升级路径;3) JVM内置锁优化技术(锁消除、锁粗化等)及适用场景;4) 通过JOL工具可视化锁状态变化,JMH基准测试对比不同锁性能;5) 电商库存扣减实战案例,展示乐观锁与悲观锁的应用;6) 生产环境调优建议,强调优先无锁设计、缩小锁粒度等原则。

一、锁优化的核心基石:对象头与内存布局

在JVM中,synchronized的锁状态完全存储在Java对象的对象头中,理解对象头的结构是拆解锁膨胀机制的前提。64位JVM默认开启压缩指针时,对象头由Mark Word(标记字段)Klass Pointer(类型指针)两部分组成,数组对象还会额外存储数组长度。其中Mark Word是锁实现的核心,固定占用8字节,存储了对象的哈希码、分代年龄、锁状态、线程ID等核心信息。

1.1 Mark Word的状态结构

Mark Word的存储结构会随着锁状态的变化动态复用,不同锁状态下的位分配如下表所示:

锁状态 高25位 31位分代年龄 1位偏向锁位 2位锁标记位
无锁 未使用 分代年龄 0 01
偏向锁 持有锁的线程ID 分代年龄 1 01
轻量级锁 指向栈中LockRecord的指针 - - 00
重量级锁 指向Monitor对象的指针 - - 10
GC标记 - - 11

核心规则:锁标记位是判断锁状态的核心依据,其中无锁与偏向锁共用01标记位,通过偏向锁位做二次区分。

1.2 管程模型Monitor

重量级锁的底层实现依赖于Monitor管程对象,每个重量级锁都会关联一个独立的Monitor,其核心结构包含三个关键部分:

  • _owner:指向当前持有锁的线程
  • _EntryList:入口集,存储竞争锁失败被阻塞的线程
  • _WaitSet:等待集,存储调用wait()方法进入等待状态的线程

Monitor的底层依赖操作系统的互斥量(mutex)实现,涉及用户态与内核态的切换,单次切换的开销约为数千纳秒,这也是重量级锁性能开销大的核心原因。

二、锁膨胀全链路流程与底层实现

锁膨胀的本质是JVM针对不同的线程竞争场景,自动选择开销最低的锁实现,当竞争加剧时,锁会从低开销状态逐步向高开销状态升级。整个流程遵循固定的升级路径,仅在GC安全点会发生锁降级。

2.1 锁膨胀全流程

2.2 各锁状态的底层实现与适用场景

2.2.1 无锁状态

无锁状态下,对象的Mark Word仅存储分代年龄、未使用的高位空间,偏向锁位为0,锁标记位为01。此时对象没有被任何线程加锁,是所有对象的初始状态。

2.2.2 偏向锁

偏向锁的核心设计理念是:无竞争场景下,消除同步操作的所有开销。其适用场景为单线程反复获取同一把锁,无多线程竞争。

  • 核心原理:第一个获取锁的线程,会通过CAS将自己的线程ID写入对象的Mark Word,将对象标记为偏向当前线程。后续该线程再次获取锁时,仅需比对Mark Word中的线程ID,无需任何CAS操作,开销仅为几次内存读取,和无锁操作几乎无差别。
  • 可重入实现:线程重入时,会在当前栈帧中创建一个无内容的Lock Record,作为重入计数的凭证,无需执行CAS操作,重入次数由栈中Lock Record的数量决定。
  • 偏向撤销:当有第二个线程尝试竞争该锁时,会触发偏向撤销。偏向撤销必须在GC安全点执行,会暂停所有正在运行的线程,校验持有偏向锁的线程是否存活,若线程已退出则重置对象为无锁状态;若线程仍存活则升级为轻量级锁。
  • 关键特性:JDK15及以上版本默认关闭偏向锁,需通过JVM参数-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0手动开启。高并发场景下,偏向撤销的STW开销远大于偏向锁带来的收益,因此高并发系统不建议开启。

2.2.3 轻量级锁

轻量级锁的核心设计理念是:低竞争场景下,通过用户态CAS避免内核态的开销。其适用场景为多个线程交替获取锁,同一时刻无多线程同时竞争。

  • 加锁流程:
  1. 线程在当前栈帧中创建名为Lock Record的空间,用于存储对象当前Mark Word的拷贝(官方称为Displaced Mark Word)。
  2. 线程通过CAS尝试将对象的Mark Word替换为指向当前栈中Lock Record的指针。
  3. 若CAS成功,当前线程获取轻量级锁,将锁标记位设置为00。
  4. 若CAS失败,说明存在竞争,线程会进入自适应自旋阶段,反复尝试CAS获取锁。
  • 自适应自旋:自旋的次数不是固定值,JVM会根据同一个锁上一次的自旋成功率与持有锁线程的状态动态调整。若上一次自旋成功获取了锁,本次会增加自旋次数;若上一次自旋失败,本次会直接跳过自旋,升级为重量级锁。
  • 可重入实现:线程每次重入都会在栈帧中创建一个新的Lock Record,存储Displaced Mark Word,重入次数由栈中Lock Record的数量决定。
  • 锁释放:线程每次退出同步块都会释放一个Lock Record,当栈中所有Lock Record都被释放后,通过CAS将Displaced Mark Word还原到对象头中。若还原时存在竞争,锁会直接膨胀为重量级锁。

2.2.4 重量级锁

重量级锁是锁膨胀的最终阶段,核心设计理念是:高竞争场景下,通过内核态互斥量保证线程安全,避免CPU空转。其适用场景为多线程同时竞争同一把锁,竞争激烈。

  • 核心原理:锁膨胀为重量级锁时,JVM会为对象创建关联的Monitor对象,将对象的Mark Word替换为指向Monitor的指针,锁标记位设置为10。竞争失败的线程会被加入Monitor的EntryList,进入内核态阻塞状态,直到持有锁的线程释放锁后被唤醒。
  • 可重入实现:Monitor对象中维护了_recursions字段,记录锁的重入次数,线程每次重入该字段加1,每次解锁减1,减至0时才会真正释放锁,唤醒EntryList中的等待线程。
  • 内存语义:重量级锁的加锁与解锁操作具备完整的Happens-Before语义,解锁操作会将线程工作内存中的所有修改刷新到主内存,加锁操作会将工作内存失效,从主内存中读取最新数据,保证多线程之间的可见性、原子性与有序性。

2.3 锁降级机制

很多资料中提到锁膨胀是单向不可逆的,这个说法并不准确。JVM会在GC安全点,对无竞争的锁进行降级:

  • 若重量级锁关联的Monitor没有线程持有,也没有线程在EntryList中等待,GC时会将其降级为无锁状态。
  • 轻量级锁若没有线程竞争,GC时也会重置为无锁状态。 锁降级仅发生在GC过程中,不会在锁释放的过程中实时发生,因此常规业务场景中,我们仍可以认为锁的升级是单向的。

三、JVM内置锁优化补充机制

除了锁膨胀机制,JVM还提供了四项核心的锁优化技术,进一步降低同步操作的性能开销。

3.1 锁消除

锁消除是JIT编译器的优化技术,核心逻辑是:通过逃逸分析,判断某把锁的对象仅能被一个线程访问,不存在多线程竞争的可能,JIT会直接消除该锁的同步操作,避免无意义的加锁解锁开销。

package com.jam.demo;
import lombok.extern.slf4j.Slf4j;
/**
* 锁消除演示示例
* @author ken
*/

@Slf4j
public class LockEliminationDemo {
   /**
    * 锁消除场景演示
    * StringBuffer的append方法是synchronized修饰的
    * 但sb是局部变量,不会逃逸出当前方法,不存在多线程竞争
    * JIT编译时会直接消除append方法的锁操作
    */

   public String lockEliminationTest() {
       StringBuffer sb = new StringBuffer();
       sb.append("a");
       sb.append("b");
       sb.append("c");
       return sb.toString();
   }
   public static void main(String[] args) {
       LockEliminationDemo demo = new LockEliminationDemo();
       // 触发JIT编译
       for (int i = 0; i < 100000; i++) {
           demo.lockEliminationTest();
       }
   }
}

锁消除的开启需要满足两个条件:JIT编译开启(默认开启)、逃逸分析开启(JVM参数-XX:+DoEscapeAnalysis,默认开启)。

3.2 锁粗化

锁粗化的核心逻辑是:当JIT检测到一系列连续的加锁解锁操作,都是针对同一个锁对象,会将锁的范围粗化到整个操作序列的外部,避免频繁的加锁解锁带来的性能开销。

package com.jam.demo;
import lombok.extern.slf4j.Slf4j;
/**
* 锁粗化演示示例
* @author ken
*/

@Slf4j
public class LockCoarseningDemo {
   private final StringBuffer sb = new StringBuffer();
   /**
    * 锁粗化场景演示
    * 循环内反复对同一个对象加锁解锁
    * JIT会将锁粗化到循环外部,仅加锁解锁一次
    */

   public void lockCoarseningTest() {
       for (int i = 0; i < 100; i++) {
           sb.append(i);
       }
   }
   public static void main(String[] args) {
       LockCoarseningDemo demo = new LockCoarseningDemo();
       // 触发JIT编译
       for (int i = 0; i < 100000; i++) {
           demo.lockCoarseningTest();
       }
   }
}

锁粗化通过JVM参数-XX:+EliminateLocks开启,默认开启。

3.3 自适应自旋

自适应自旋在轻量级锁章节已经提及,核心是JVM会根据锁的历史竞争情况动态调整自旋次数,避免固定自旋次数带来的CPU空转或频繁内核态切换问题。该特性通过-XX:+UseAdaptiveSpinning开启,默认开启,不建议手动修改。

3.4 逃逸分析

逃逸分析是所有锁优化的基础,JVM通过逃逸分析判断对象的作用范围:

  • 不逃逸:对象仅在当前方法内使用,不会被其他线程访问。
  • 方法逃逸:对象被传递到其他方法中,但不会被外部线程访问。
  • 线程逃逸:对象被外部线程访问,存在多线程竞争的可能。 只有不逃逸的对象,才会触发锁消除优化。逃逸分析通过-XX:+DoEscapeAnalysis开启,默认开启。

四、锁膨胀可视化演示与代码实战

本节通过JOL(Java Object Layout)工具,可视化展示锁膨胀的完整过程。

4.1 环境依赖配置

首先在maven的pom.xml中添加所需依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.4</version>
       <relativePath/>
   </parent>
   <groupId>com.jam.demo</groupId>
   <artifactId>jvm-lock-demo</artifactId>
   <version>1.0.0</version>
   <properties>
       <maven.compiler.source>17</maven.compiler.source>
       <maven.compiler.target>17</maven.compiler.target>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <mybatis-plus.version>3.5.6</mybatis-plus.version>
       <fastjson2.version>2.0.52</fastjson2.version>
       <guava.version>33.1.0-jre</guava.version>
       <jol.version>0.17</jol.version>
       <jmh.version>1.37</jmh.version>
   </properties>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
           <version>2.5.0</version>
       </dependency>
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>
       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
           <scope>runtime</scope>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.18.32</version>
           <scope>provided</scope>
       </dependency>
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
           <version>${fastjson2.version}</version>
       </dependency>
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>
       <dependency>
           <groupId>org.openjdk.jol</groupId>
           <artifactId>jol-core</artifactId>
           <version>${jol.version}</version>
       </dependency>
       <dependency>
           <groupId>org.openjdk.jmh</groupId>
           <artifactId>jmh-core</artifactId>
           <version>${jmh.version}</version>
       </dependency>
       <dependency>
           <groupId>org.openjdk.jmh</groupId>
           <artifactId>jmh-generator-annprocess</artifactId>
           <version>${jmh.version}</version>
           <scope>provided</scope>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.13.0</version>
               <configuration>
                   <source>17</source>
                   <target>17</target>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

4.2 锁膨胀全流程可视化代码

package com.jam.demo;
import org.openjdk.jol.info.ClassLayout;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
/**
* 锁膨胀全流程可视化演示
* @author ken
*/

@Slf4j
public class LockInflationDemo {
   private static final Object lockObj = new Object();
   public static void main(String[] args) throws InterruptedException {
       log.info("1. 无锁状态,对象头信息:");
       printObjectHead();
       Thread.sleep(1000);
       // 单线程加锁,演示轻量级锁(JDK17默认关闭偏向锁)
       synchronized (lockObj) {
           log.info("2. 单线程持有锁,轻量级锁状态,对象头信息:");
           printObjectHead();
       }
       log.info("3. 轻量级锁释放后,对象头信息:");
       printObjectHead();
       Thread.sleep(1000);
       // 多线程竞争,演示膨胀为重量级锁
       CountDownLatch latch = new CountDownLatch(2);
       Thread thread1 = new Thread(() -> {
           for (int i = 0; i < 100; i++) {
               synchronized (lockObj) {
                   if (i == 50) {
                       log.info("4. 多线程竞争下,重量级锁状态,对象头信息:");
                       printObjectHead();
                   }
               }
           }
           latch.countDown();
       });
       Thread thread2 = new Thread(() -> {
           for (int i = 0; i < 100; i++) {
               synchronized (lockObj) {
               }
           }
           latch.countDown();
       });
       thread1.start();
       thread2.start();
       latch.await();
       Thread.sleep(1000);
       log.info("5. 重量级锁释放后,对象头信息:");
       printObjectHead();
   }
   /**
    * 打印对象头信息
    */

   private static void printObjectHead() {
       System.out.println(ClassLayout.parseInstance(lockObj).toPrintable());
   }
}

运行说明:

  • 若需演示偏向锁,需添加JVM启动参数:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
  • 控制台输出的对象头中,前8字节为Mark Word,可通过锁标记位判断当前锁状态。

4.3 高并发场景锁性能基准测试

通过JMH官方基准测试工具,对比不同锁实现的吞吐量性能,代码如下:

package com.jam.demo;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.ReentrantLock;
/**
* 高并发锁性能基准测试
* @author ken
*/

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@Threads(10)
@State(Scope.Benchmark)
public class LockPerformanceBenchmark {
   private int syncCount = 0;
   private final Object lockObj = new Object();
   private final AtomicInteger atomicCount = new AtomicInteger(0);
   private final LongAdder adderCount = new LongAdder();
   private final ReentrantLock reentrantLock = new ReentrantLock();
   private int lockCount = 0;
   @Benchmark
   public void syncLockTest() {
       synchronized (lockObj) {
           syncCount++;
       }
   }
   @Benchmark
   public void atomicIntegerTest() {
       atomicCount.incrementAndGet();
   }
   @Benchmark
   public void longAdderTest() {
       adderCount.increment();
   }
   @Benchmark
   public void reentrantLockTest() {
       reentrantLock.lock();
       try {
           lockCount++;
       } finally {
           reentrantLock.unlock();
       }
   }
   public static void main(String[] args) throws RunnerException {
       Options options = new OptionsBuilder()
               .include(LockPerformanceBenchmark.class.getSimpleName())
               .build()
;
       new Runner(options).run();
   }
}

测试结论:

  • 无竞争场景下:偏向锁性能最优,与无锁操作几乎无差别,其次是轻量级锁、AtomicInteger、ReentrantLock。
  • 低竞争场景下:轻量级锁性能优于重量级锁,AtomicInteger性能优于synchronized与ReentrantLock。
  • 高竞争场景下:LongAdder性能最优,其次是AtomicInteger,ReentrantLock与重量级锁synchronized性能接近。

五、高并发业务场景锁调优实战

本节以电商核心的库存扣减场景为例,结合MyBatisPlus与MySQL8.0,演示高并发场景下的锁调优方案。

5.1 数据库表结构

CREATE TABLE `t_inventory` (
 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
 `sku_id` bigint NOT NULL COMMENT '商品SKU ID',
 `stock_num` int NOT NULL DEFAULT '0' COMMENT '库存数量',
 `version` int NOT NULL DEFAULT '0' COMMENT '乐观锁版本号',
 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 PRIMARY KEY (`id`),
 UNIQUE KEY `uk_sku_id` (`sku_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品库存表';

5.2 实体类定义

package com.jam.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 库存实体类
* @author ken
*/

@Data
@TableName("t_inventory")
@Schema(description = "商品库存实体")
public class Inventory {
   @TableId(type = IdType.AUTO)
   @Schema(description = "主键ID", example = "1")
   private Long id;
   @Schema(description = "商品SKU ID", example = "1001")
   private Long skuId;
   @Schema(description = "库存数量", example = "1000")
   private Integer stockNum;
   @Version
   @Schema(description = "乐观锁版本号", example = "0")
   private Integer version;
   @TableField(fill = FieldFill.INSERT)
   @Schema(description = "创建时间")
   private LocalDateTime createTime;
   @TableField(fill = FieldFill.INSERT_UPDATE)
   @Schema(description = "更新时间")
   private LocalDateTime updateTime;
}

5.3 Mapper层定义

package com.jam.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.Inventory;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/**
* 库存Mapper接口
* @author ken
*/

public interface InventoryMapper extends BaseMapper<Inventory> {
   /**
    * 悲观锁扣减库存
    * @param skuId 商品SKU ID
    * @param num 扣减数量
    * @return 影响行数
    */

   @Update("UPDATE t_inventory SET stock_num = stock_num - #{num} WHERE sku_id = #{skuId} AND stock_num >= #{num}")
   int deductStockByPessimisticLock(@Param("skuId") Long skuId, @Param("num") Integer num);
}

5.4 Service层实现

package com.jam.demo.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jam.demo.entity.Inventory;
import com.jam.demo.mapper.InventoryMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.ObjectUtils;
/**
* 库存服务实现类
* @author ken
*/

@Slf4j
@Service
@RequiredArgsConstructor
public class InventoryService {
   private final InventoryMapper inventoryMapper;
   private final PlatformTransactionManager transactionManager;
   /**
    * 乐观锁扣减库存
    * @param skuId 商品SKU ID
    * @param deductNum 扣减数量
    * @return 扣减结果
    */

   public boolean deductStockByOptimisticLock(Long skuId, Integer deductNum) {
       if (ObjectUtils.isEmpty(skuId) || ObjectUtils.isEmpty(deductNum) || deductNum <= 0) {
           log.error("库存扣减参数异常,skuId:{},deductNum:{}", skuId, deductNum);
           return false;
       }
       // 编程式事务定义
       DefaultTransactionDefinition def = new DefaultTransactionDefinition();
       def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
       def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
       TransactionStatus status = transactionManager.getTransaction(def);
       try {
           // 查询库存信息
           Inventory inventory = inventoryMapper.selectOne(
                   new LambdaQueryWrapper<Inventory>()
                           .eq(Inventory::getSkuId, skuId)
           );
           if (ObjectUtils.isEmpty(inventory)) {
               log.error("商品库存不存在,skuId:{}", skuId);
               transactionManager.rollback(status);
               return false;
           }
           if (inventory.getStockNum() < deductNum) {
               log.warn("商品库存不足,skuId:{},currentStock:{},deductNum:{}", skuId, inventory.getStockNum(), deductNum);
               transactionManager.rollback(status);
               return false;
           }
           // 乐观锁更新,版本号自动递增
           LambdaUpdateWrapper<Inventory> updateWrapper = new LambdaUpdateWrapper<>();
           updateWrapper.eq(Inventory::getId, inventory.getId())
                   .eq(Inventory::getVersion, inventory.getVersion())
                   .setSql("stock_num = stock_num - " + deductNum);
           int updateRows = inventoryMapper.update(null, updateWrapper);
           if (updateRows > 0) {
               transactionManager.commit(status);
               log.info("乐观锁库存扣减成功,skuId:{},deductNum:{}", skuId, deductNum);
               return true;
           } else {
               transactionManager.rollback(status);
               log.warn("乐观锁更新失败,存在并发竞争,skuId:{}", skuId);
               return false;
           }
       } catch (Exception e) {
           transactionManager.rollback(status);
           log.error("库存扣减异常,skuId:{}", skuId, e);
           return false;
       }
   }
   /**
    * 悲观锁扣减库存
    * @param skuId 商品SKU ID
    * @param deductNum 扣减数量
    * @return 扣减结果
    */

   public boolean deductStockByPessimisticLock(Long skuId, Integer deductNum) {
       if (ObjectUtils.isEmpty(skuId) || ObjectUtils.isEmpty(deductNum) || deductNum <= 0) {
           log.error("库存扣减参数异常,skuId:{},deductNum:{}", skuId, deductNum);
           return false;
       }
       DefaultTransactionDefinition def = new DefaultTransactionDefinition();
       def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
       def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
       TransactionStatus status = transactionManager.getTransaction(def);
       try {
           int updateRows = inventoryMapper.deductStockByPessimisticLock(skuId, deductNum);
           if (updateRows > 0) {
               transactionManager.commit(status);
               log.info("悲观锁库存扣减成功,skuId:{},deductNum:{}", skuId, deductNum);
               return true;
           } else {
               transactionManager.rollback(status);
               log.warn("库存不足,扣减失败,skuId:{},deductNum:{}", skuId, deductNum);
               return false;
           }
       } catch (Exception e) {
           transactionManager.rollback(status);
           log.error("库存扣减异常,skuId:{}", skuId, e);
           return false;
       }
   }
}

5.5 Controller层实现

package com.jam.demo.controller;
import com.jam.demo.service.InventoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 库存操作接口
* @author ken
*/

@RestController
@RequestMapping("/inventory")
@RequiredArgsConstructor
@Tag(name = "库存管理", description = "商品库存扣减相关接口")
public class InventoryController {
   private final InventoryService inventoryService;
   @PostMapping("/deduct/optimistic")
   @Operation(summary = "乐观锁扣减库存", description = "高并发场景推荐使用,无锁阻塞开销")
   public boolean deductByOptimisticLock(
           @Parameter(description = "商品SKU ID", required = true)
@RequestParam Long skuId,
           @Parameter(description = "扣减数量", required = true) @RequestParam Integer deductNum) {
       return inventoryService.deductStockByOptimisticLock(skuId, deductNum);
   }
   @PostMapping("/deduct/pessimistic")
   @Operation(summary = "悲观锁扣减库存", description = "并发量较低、数据一致性要求极高的场景使用")
   public boolean deductByPessimisticLock(
           @Parameter(description = "商品SKU ID", required = true)
@RequestParam Long skuId,
           @Parameter(description = "扣减数量", required = true) @RequestParam Integer deductNum) {
       return inventoryService.deductStockByPessimisticLock(skuId, deductNum);
   }
}

5.6 场景调优方案

  1. 高并发秒杀场景:优先使用乐观锁方案,避免JVM锁与数据库行锁带来的阻塞开销,同时结合预扣库存、库存分段、消息队列削峰等方案,进一步降低锁竞争。
  2. 低并发高一致性场景:可使用悲观锁方案,保证数据强一致性,同时缩小锁的范围,避免长事务持有锁。
  3. JVM锁调优:避免在分布式场景下使用JVM锁,分布式场景优先使用分布式锁(如Redis分布式锁、Zookeeper分布式锁);单机场景优先缩小锁的粒度与持有时间,降低锁竞争的概率。

六、易混淆技术点明确区分

6.1 轻量级锁 ≠ 自旋锁

轻量级锁是JVM定义的一种锁状态,核心是通过用户态CAS实现同步;自旋是锁竞争失败后的一种重试机制,不仅轻量级锁会使用自旋,重量级锁在进入内核态阻塞之前也会尝试自旋。自旋是一种优化手段,不是锁的状态,二者不能划等号。

6.2 偏向锁撤销 ≠ 锁释放

偏向锁撤销是指多线程竞争时,取消对象的偏向状态,将锁升级为轻量级锁的过程,该过程需要在GC安全点执行,会触发STW;锁释放是线程执行完同步块,主动释放锁的过程,不会触发STW。二者是完全不同的操作,不能混淆。

6.3 用户态锁 vs 内核态锁

  • 用户态锁:偏向锁、轻量级锁的实现完全在用户态完成,不需要操作系统内核介入,不会发生用户态与内核态的切换,开销极低。
  • 内核态锁:重量级锁依赖操作系统的互斥量实现,线程的阻塞与唤醒都需要内核介入,会发生用户态与内核态的切换,开销极大。

6.4 synchronized vs ReentrantLock

特性 synchronized ReentrantLock
实现层面 JVM层面实现,由字节码指令控制 JDK层面实现,基于AQS框架
锁优化 支持锁膨胀、锁消除、锁粗化等全链路优化 仅支持公平锁/非公平锁、可中断等特性
可重入性 支持,底层通过Mark Word/Monitor实现 支持,底层通过AQS的state字段实现
公平锁 仅支持非公平锁 支持公平锁与非公平锁,可通过构造方法指定
高级特性 支持可中断获取锁、超时获取锁、多条件变量
释放方式 自动释放,异常时也会释放 必须手动在finally块中释放,否则会造成死锁

七、生产环境锁调优最佳实践

7.1 锁竞争问题排查工具

  1. jstack:JDK自带的线程堆栈分析工具,可查看线程的锁持有状态、锁等待状态,快速定位死锁与锁竞争问题。
  2. Arthas:阿里开源的Java诊断工具,可通过vmtool命令查看对象头的锁状态,通过thread命令查看线程的锁竞争情况。
  3. JProfiler/AsyncProfiler:性能分析工具,可采集锁的持有时间、竞争次数,定位性能瓶颈。

7.2 核心调优原则

  1. 优先无锁设计:最优的锁优化永远是消除锁。通过ThreadLocal、不可变对象、无锁并发工具类(如LongAdder、ConcurrentHashMap)等方案,避免锁竞争。
  2. 缩小锁粒度:仅对需要同步的代码块加锁,避免对整个方法加锁,减少锁的持有时间。
  3. 降低锁的持有时间:将耗时操作(如IO操作、网络调用)移出同步块,避免持有锁的同时执行耗时操作,加剧锁竞争。
  4. 避免锁嵌套:严格控制加锁顺序,所有线程按照相同的顺序加锁,避免死锁问题。
  5. 合理选择锁类型
  • 单线程无竞争场景:开启偏向锁,获得最优性能。
  • 多线程交替加锁场景:使用轻量级锁,避免内核态开销。
  • 高并发竞争场景:优先使用乐观锁、JUC并发工具类,避免重量级锁。
  1. JVM参数调优
  • 高并发场景:保持JDK17默认配置,关闭偏向锁,避免偏向撤销的STW开销。
  • 低延迟系统:通过-XX:PreBlockSpin调整自旋次数,减少内核态切换的概率(不建议手动调整,优先使用自适应自旋)。
  • 开启逃逸分析与锁消除:保持默认开启状态,JIT会自动优化无意义的锁操作。

7.3 分布式场景注意事项

JVM锁仅能保证单机内的线程安全,分布式场景下必须使用分布式锁,避免出现数据不一致问题。分布式锁优先选择Redis Redlock、Zookeeper分布式锁等成熟方案,同时保证锁的可重入性、超时释放、死锁预防等特性。

八、总结

JVM的锁膨胀机制,本质上是JVM在性能与线程安全之间做的动态平衡,针对不同的竞争场景,提供了不同开销的锁实现。从无锁到偏向锁、轻量级锁、重量级锁,每一次锁膨胀都是JVM针对竞争加剧做出的自适应调整。

理解锁膨胀的底层逻辑,不仅能打破对synchronized的刻板印象,更能在开发中写出更高效的同步代码,在生产环境出现锁竞争问题时,快速定位根因,给出针对性的调优方案。在高并发系统设计中,我们需要始终记住:锁优化的核心不是把锁的性能调到极致,而是通过合理的架构设计,从根源上消除锁竞争。

目录
相关文章
|
8天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5223 9
|
16天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
21237 115
|
12天前
|
人工智能 安全 前端开发
Team 版 OpenClaw:HiClaw 开源,5 分钟完成本地安装
HiClaw 基于 OpenClaw、Higress AI Gateway、Element IM 客户端+Tuwunel IM 服务器(均基于 Matrix 实时通信协议)、MinIO 共享文件系统打造。
8098 10

热门文章

最新文章