1. 概述

Project Lombok 是一个 Java 库,通过提供各种注解来生成标准方法和功能,从而减少样板代码。例如,我们可以用 Lombok 自动生成 getter/setter、构造函数,甚至引入建造者模式等设计模式。

本教程将学习 Lombok 1.18.32 版本引入的 @Locked 注解的使用方法。

2. 为什么需要 @Locked 注解?

首先理解 @Locked 注解的必要性:

Java 21 引入了虚拟线程来简化并发应用的实现、维护和调试。与传统线程不同,虚拟线程由 JVM 而非操作系统管理,因此:

  • ✅ 无需系统调用即可分配
  • ✅ 不依赖操作系统上下文切换

但需警惕虚拟线程的潜在性能问题:

  • 当阻塞操作发生在 synchronized 块或方法内时,仍会阻塞操作系统线程
  • 这种情况称为 线程钉死(Pinning)
  • ✅ 若阻塞操作在 synchronized 块外,则不会引发问题

线程钉死会显著影响应用性能,尤其当:

  • 阻塞操作频繁调用
  • 阻塞操作耗时较长

⚠️ 短暂且不频繁的阻塞操作通常不会造成问题

解决方案:用 ReentrantLock 替代 synchronized —— 这正是 Lombok 新注解的用武之地。

3. 理解 @Locked 注解

简单来说:

  • @Locked 是 ReentrantLock 的变体实现
  • 专为虚拟线程优化设计

核心特性:

  • 仅可用于静态或实例方法
  • 自动将方法体包装为获取锁的代码块
  • 可指定 ReentrantLock 类型的字段作为锁对象
  • 方法执行结束后自动释放锁

4. 依赖配置

pom.xml 中添加 lombok 依赖:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.34</version>
    <scope>provided</scope>
</dependency>

⚠️ 需使用 1.18.32 或更高版本

5. 使用方法

创建 Counter 类,包含 increment()get() 方法:

public class Counter {
    private int counter = 0;
    private ReentrantLock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public int get() {
        lock.lock();
        try {
            return counter;
        } finally {
            lock.unlock();
        }
    }
}

counter++ 不是原子操作,需加锁保证多线程环境下的可见性

@Locked 简化代码:

@Locked
public void increment() {
    counter++;
}

@Locked
public int get() {
    return counter;
}

工作原理

  1. Lombok 自动创建 ReentrantLock 字段(实例方法用 $lock,静态方法用 $LOCK
  2. 将方法体包装为获取锁的代码块
  3. 方法退出时自动释放锁

⚠️ 多个 @Locked 方法默认共享同一把锁。如需独立锁,需显式声明 ReentrantLock 字段并指定名称

测试用例:

@Test
void givenCounter_whenIncrementCalledMultipleTimes_thenReturnCorrectResult() throws InterruptedException {
    Counter counter = new Counter();

    Thread.Builder builder = Thread.ofVirtual().name("worker-", 0);
    Runnable task = counter::increment;

    Thread t1 = builder.start(task);
    t1.join();

    Thread t2 = builder.start(task);
    t2.join();

    assertEquals(2, counter.get());
}

5.1. @Locked.Read 和 @Locked.Write 注解

替代 ReentrantReadWriteLock 的专用注解:

  • @Locked.Read:获取读锁
  • @Locked.Write:获取写锁

改造 Counter 类:

@Locked.Write
public void increment() {
    counter++;
}

@Locked.Read
public int get() {
    return counter;
}

✅ 读写分离锁可显著提升性能,尤其在高负载读写场景

⚠️ 同一类中混用 @Locked.Read/@Locked.Write 时,需显式指定锁字段名避免冲突

6. @Locked 与 @Synchronized 的区别

Lombok 还提供类似功能的 @Synchronized 注解,主要差异:

特性 @Locked @Synchronized
替代对象 ReentrantLock synchronized 关键字
锁对象 Lombok 生成的专用字段 默认锁定 this 或类对象
虚拟线程兼容性 ✅ 完全兼容 ❌ 可能导致线程钉死
适用场景 虚拟线程环境 传统线程环境

⚠️ 虚拟线程环境下强烈推荐使用 @Locked

7. 总结

本文系统介绍了 Lombok 的 @Locked 注解:

  • ✅ 为虚拟线程优化而生
  • ✅ 简化 ReentrantLock 的使用
  • ✅ 支持读写分离锁(@Locked.Read/@Locked.Write)
  • ✅ 比 @Synchronized 更适合虚拟线程场景

完整源码见 GitHub 仓库


原始标题:A Guide to @Locked in Lombok | Baeldung