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

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

在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并发工具类,避免重量级锁。

  6. JVM参数调优

    • 高并发场景:保持JDK17默认配置,关闭偏向锁,避免偏向撤销的STW开销。

    • 低延迟系统:通过-XX:PreBlockSpin调整自旋次数,减少内核态切换的概率(不建议手动调整,优先使用自适应自旋)。

    • 开启逃逸分析与锁消除:保持默认开启状态,JIT会自动优化无意义的锁操作。

7.3 分布式场景注意事项

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

八、总结

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

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

相关推荐
qq_417695052 小时前
用Python创建一个Discord聊天机器人
jvm·数据库·python
2401_874732532 小时前
使用Scrapy框架构建分布式爬虫
jvm·数据库·python
柒.梧.2 小时前
JVM核心知识点部分总结(快速入门)
jvm
wuqingshun3141592 小时前
产生死锁的四个必要条件
java·jvm
Predestination王瀞潞2 小时前
3. JVM(Java Virtual Machine,Java 虚拟机):从核心架构到运行机制的全方位剖析
java·jvm·架构
2501_9454235410 小时前
Django全栈开发入门:构建一个博客系统
jvm·数据库·python
2301_7938046915 小时前
更优雅的测试:Pytest框架入门
jvm·数据库·python
ole ' ola15 小时前
lambda表达式
java·前端·jvm
2301_8154829316 小时前
用Python实现自动化的Web测试(Selenium)
jvm·数据库·python