Redis 加锁
如果要使用redis给一个创建订单的方法添加分布式锁,该如何做呢?
就是,只需要创建一个固定的key,使用这个key去加锁即可。但是这样锁的粒度就太粗了,会影响别的订单的创建。
然后就会考虑创建一个细粒度锁,在设计key时,有个固定的前缀,后面拼接订单号。以此来加锁,基本就可以了。
Java 加锁
那么,当使用Java锁时,如何实现细粒度锁呢?参考代码如下:
private static Map<String, Lock> locks = new ConcurrentHashMap<>();
public void createOrder(String orderNo) {
Lock lock = locks.computeIfAbsent("lock:order:"+orderNo, k -> new ReentrantLock());
lock.lock();
try {
// do something
} finally {
lock.unlock();
}
}
以上代码的写法,既实现了细粒度锁,又同时保证了获取锁的原子性。看起来功能实现得挺好。
是否还有问题呢?
有经验的你可能很快就看出来了,何时销毁锁的实例呢?
由于订单号是无限的,锁的实例也会创建很多,且基本上锁使用一次就不再使用了。由于锁实例一直被引用,导致无法回收。
既然想要回收,能否像Redis加锁一样,类似缓存过期,有个机制自动销毁不用的锁实例呢。
一般有两种方案:
- 方案一:手搓,就是自己写代码控制锁实例的过期,不推荐。
- 方案二:借助本地缓存工具,比如 Caffeine。根据实际情况调整缓存参数配置。
方案二,示例代码如下:
private static Cache<String, Lock> lockCache = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.maximumSize(1000)
.build();
public void createOrder(String orderNo) {
String lockKey = "lock:order:" + orderNo;
Lock lock = lockCache.get(lockKey, k -> new ReentrantLock());
lock.lock();
try {
// do something
} finally {
lock.unlock();
}
}