本文皆为个人原创,请尊重创作,未经许可不得转载。
本文将详细解析一个高效、可重入的JVM级锁工具实现,包括核心锁工具类、注解式声明锁以及AOP切面实现,助你掌握Java并发编程的高级技巧。
一、锁工具的核心需求
为什么需要自研分布式锁工具?
在分布式系统中,跨进程资源竞争常依赖Redis/ZooKeeper等中间件。但在单服务高并发场景 (如订单处理、库存扣减)中,JVM级别的锁工具能提供更高性能(避免网络IO)和更低延迟。LockUtil
正是针对这一场景的轻量级解决方案
在多线程环境下且单机服务,需要一种机制来保证资源的安全访问。一个完善的锁工具应该具备:
- 可重入性:同一线程可多次获取同一把锁
- 公平性选择:支持公平锁与非公平锁
- 锁清理机制:自动清理闲置锁,避免内存泄漏
- 超时控制:防止死锁
- 易用性:简洁的API和注解支持
二、核心组件解析
1. LockUtil:锁工具核心类
java
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* JVM级锁工具
*
* @Author:Derek_Smart
* @Date:2025/7/10 14:06
*/
public class LockUtil implements AutoCloseable {
// 使用ConcurrentHashMap存储锁对象,键为锁标识
private final ConcurrentHashMap<String, LockHolder> lockMap = new ConcurrentHashMap<>();
// 清理线程池
private final ScheduledExecutorService cleaner;
private final boolean fair; // 是否公平锁
private final long cleanInterval; // 清理间隔(毫秒)
private final long idleTimeout; // 空闲超时(毫秒)
private volatile boolean closed = false;
// 构造方法
public LockUtil(boolean fair, long cleanInterval, long idleTimeout, TimeUnit unit) {
this.fair = fair;
this.cleanInterval = unit.toMillis(cleanInterval);
this.idleTimeout = unit.toMillis(idleTimeout);
this.cleaner = Executors.newSingleThreadScheduledExecutor(
r -> new Thread(r, "lock-cleaner-daemon")
);
startCleaner();
}
// 启动清理任务
private void startCleaner() {
cleaner.scheduleAtFixedRate(
this::safeCleanup,
cleanInterval, cleanInterval, TimeUnit.MILLISECONDS
);
}
// 安全清理方法
private void safeCleanup() {
long now = System.currentTimeMillis();
lockMap.entrySet().removeIf(entry -> {
LockHolder holder = entry.getValue();
return !holder.isInUse() &&
(now - holder.getLastAccessTime() > idleTimeout);
});
}
// 加锁并执行操作(核心方法)
public <T> T execute(String key, long waitTime, Callable<T> action) throws Exception {
LockHolder holder = lockMap.compute(key, (k, v) ->
(v == null) ? new LockHolder(fair) : v
);
if (!holder.acquireLock(waitTime, TimeUnit.MILLISECONDS)) {
throw new TimeoutException("Lock timeout for: " + key);
}
try {
return action.call();
} finally {
holder.releaseLock();
}
}
// 内部锁持有者类
private class LockHolder {
private final ReentrantLock lock;
private final AtomicInteger holdCount = new AtomicInteger(0);
private volatile long lastAccessTime;
LockHolder(boolean fair) {
this.lock = new ReentrantLock(fair);
updateAccessTime();
}
// 获取锁
boolean acquireLock(long timeout, TimeUnit unit) throws InterruptedException {
if (timeout <= 0) {
lock.lock(); // 无限等待模式
holdCount.incrementAndGet();
updateAccessTime();
return true;
} else {
boolean acquired = lock.tryLock(timeout, unit);
if (acquired) {
holdCount.incrementAndGet();
updateAccessTime();
}
return acquired;
}
}
// 释放锁
void releaseLock() {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
int count = holdCount.decrementAndGet();
if (count == 0) updateAccessTime();
} else {
throw new IllegalMonitorStateException();
}
}
// 检查锁是否被占用
boolean isInUse() {
return holdCount.get() > 0 || lock.isLocked();
}
}
}

关键设计解析:
-
锁存储结构 :使用
ConcurrentHashMap
存储锁对象,键为锁标识 -
自动清理机制:
- 定时清理空闲超时的锁(
safeCleanup
方法) - 使用
ScheduledExecutorService
定期执行清理 - 仅清理未被使用且超过空闲超时时间的锁
- 定时清理空闲超时的锁(
-
锁获取策略:
- 当
waitTime <= 0
时使用无限等待模式 - 支持超时等待,避免死锁
- 当
-
资源管理 :实现
AutoCloseable
接口,正确关闭线程池
核心架构设计解析
1. 三级锁管理模型
java
// 锁管理层级关系
ConcurrentHashMap<String, LockHolder> // 全局锁池(Key: 业务标识)
└── LockHolder // 锁持有者(封装ReentrantLock)
├── ReentrantLock // 物理锁(支持公平/非公平)
└── AtomicInteger holdCount // 重入计数器
- 动态锁创建 :通过
compute()
实现锁的懒加载,避免预分配资源浪费 - 双重状态跟踪 :
holdCount
记录重入次数 +isLocked()
检测锁占用,确保清理安全性
2. 空闲锁自动回收机制
java
cleaner.scheduleAtFixedRate(this::safeCleanup, cleanInterval, TimeUnit.MILLISECONDS);
- 智能清理策略 :仅回收未被占用 且超时 的锁(
!holder.isInUse() && (now - lastAccessTime > idleTimeout)
) - 避免误删重入锁 :通过
holdCount > 0
判断锁是否真正空闲
3. ReentrantLock的精细化控制
java
// 支持公平/非公平锁选择
public LockUtil(boolean fair, long cleanInterval, long idleTimeout, TimeUnit unit) {
this.fair = fair; // true=公平锁, false=非公平锁
}
- 公平锁:按请求顺序获取,避免线程饥饿
- 非公平锁:允许插队,吞吐量更高(默认选择)
2. WithLock:声明式锁注解
java
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @Author:Derek_Smart
* @Date:2025/7/10 14:33
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WithLock {
/**
* 锁Key(支持SpEL表达式,如:`'order:' + #orderId`)
*/
String key() default "";
/**
* 最大等待时间(默认不等待)
*/
long waitTime() default 0;
/**
* 时间单位(默认毫秒)
*/
TimeUnit unit() default TimeUnit.MILLISECONDS;
}

3. LockAspect:注解实现切面
java
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* @Author:Derek_Smart
* @Date:2025/7/10 14:34
*/
@Aspect
@Component
@Slf4j
public class LockAspect {
private final LockUtil lockUtil = new LockUtil();
private final ExpressionParser parser = new SpelExpressionParser();
private final DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
@Around("@annotation(withLock)")
public Object around(ProceedingJoinPoint pjp, WithLock withLock) throws Throwable {
// 解析锁Key(支持SpEL动态参数)
String key = resolveKey(pjp, withLock.key());
long waitTime = withLock.waitTime();
TimeUnit unit = withLock.unit();
return lockUtil.execute(key, unit.toMillis(waitTime), () -> {
try {
return pjp.proceed();
} catch (Throwable e) {
log.error("LockAspect 加锁失败", e);
throw new RuntimeException(e);
}
});
}
/**
* 解析SpEL表达式生成动态Key
*/
private String resolveKey(ProceedingJoinPoint pjp, String keyExpr) {
if (keyExpr.isEmpty()) {
// 默认Key:类名+方法名
MethodSignature signature = (MethodSignature) pjp.getSignature();
return signature.getDeclaringTypeName() + "." + signature.getName();
}
// 解析SpEL表达式
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
Object[] args = pjp.getArgs();
MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(
null, method, args, nameDiscoverer
);
Expression exp = parser.parseExpression(keyExpr);
return exp.getValue(context, String.class);
}
}

关键功能解析:
-
SpEL表达式支持:动态生成锁键
- 支持从方法参数中提取值
- 示例:
@WithLock(key = "'user:' + #userId")
-
默认键策略:类名+方法名作为默认锁键
-
异常处理:捕获并记录加锁过程中的异常
-
无缝集成:通过AOP实现业务逻辑与锁管理的解耦
三、使用示例
基本使用
java
// 创建锁工具实例
LockUtil lockUtil = new LockUtil(true, 5, 30, TimeUnit.MINUTES);
try {
lockUtil.execute("resource_key", 1000, () -> {
// 需要加锁执行的业务逻辑
System.out.println("执行关键操作");
return null;
});
} catch (Exception e) {
e.printStackTrace();
}
注解式使用
java
@Service
public class OrderService {
@WithLock(key = "'order:' + #orderId", waitTime = 2, unit = TimeUnit.SECONDS)
public void updateOrder(String orderId, Order newOrder) {
// 更新订单逻辑
}
@WithLock // 使用默认键(类名+方法名)
public void processBatch() {
// 批量处理逻辑
}
}
四、设计亮点
-
智能锁清理:
- 使用守护线程定期清理
- 基于最后访问时间和使用状态判断
- 防止内存泄漏
-
动态锁键生成:
- 支持SpEL表达式
- 基于方法参数动态生成
- 默认类名+方法名策略
-
可重入支持:
- 通过
holdCount
计数器实现 - 确保同一线程可多次获取同一锁
- 通过
-
资源安全管理:
- 实现
AutoCloseable
- 正确关闭线程池
- 防止线程泄漏
- 实现
-
灵活的超时控制:
- 支持不同时间单位
- 无限等待模式(waitTime <= 0)
- 精确的超时异常处理
五、性能优化建议
- 锁粒度控制:
java
// 细粒度锁 - 推荐
@WithLock(key = "'account:' + #accountId")
// 粗粒度锁 - 谨慎使用
@WithLock(key = "'all_accounts'")
-
超时设置:
- 根据业务场景设置合理超时
- 避免过长(浪费资源)或过短(频繁超时)
-
锁统计监控:
java
// 监控活跃锁数量
int activeLocks = lockUtil.activeLockCount();
// 监控总锁数量
int totalLocks = lockUtil.totalLockCount();
-
公平性选择:
- 公平锁(fair=true):保证顺序,性能较低
- 非公平锁(fair=false):吞吐量高,可能饥饿
六、扩展思考
-
分布式锁扩展:
- 可替换LockUtil实现为Redis或Zookeeper方案
- 保持API不变,实现无缝迁移
-
锁降级/升级:
- 增加读写锁支持
- 实现锁的降级(写锁→读锁)
-
锁监控:
- 添加JMX支持
- 实时监控锁状态
-
性能统计:
- 记录锁等待时间
- 统计锁竞争情况
七、总结
本文详细解析了一个功能完备的JVM级锁工具实现,它具有以下特点:
- 功能完善:支持可重入、公平锁选择、超时控制等
- 资源安全:实现自动清理和资源关闭
- 使用便捷:提供注解式声明锁支持
- 动态灵活:支持SpEL表达式生成锁键
- 高性能:合理设计避免性能瓶颈
适用场景推荐:
- 电商库存扣减
- 财务批次记账
- 本地缓存更新
- 短任务排队处理
🔍与其他方案对比
方案 | 吞吐量 | 适用场景 | 缺点 |
---|---|---|---|
LockUtil | ⭐⭐⭐⭐ | 单服务高并发 | 不适用分布式集群 |
Redis分布式锁 | ⭐⭐⭐ | 跨进程资源竞争 | 网络延迟影响性能 |
synchronized | ⭐⭐ | 简单临界区保护 | 功能扩展性差 |
ZooKeeper锁 | ⭐⭐ | 强一致性场景 | 性能最低 |
该锁工具适用于单机环境下的资源同步控制,通过合理的锁粒度控制和超时设置,可以在保证线程安全的同时获得良好的性能表现。
本文皆为个人原创,请尊重创作,未经许可不得转载。