上一节我描述了粗粒度锁,并且描述了它的缺点。这一节我将使用细粒度锁来处理上一节中粗粒度锁所带来的问题。
- 每次锁住所有的资源,导致事务碰撞率提高,影响效率
- 一旦发生异常影响锁的释放,会产生死锁
我们可以为集合中的每个资源提供一个锁,这样可以避免每次的操作都会锁住所有的资源,其次我们为每一个锁设置一个超时时间,避免死锁情况的出现。
下面看一下代码
/**
* 锁定数据
* @param key
* @param field
* @return
*/
public Boolean lock(String key,String field,TimeUnit timeUnit,Long time,int num){
AssertLock.isTrue(!StringUtils.isEmpty(key),"key不能为空");
AssertLock.isTrue(!StringUtils.isEmpty(field),"field不能为空");
AssertLock.isTrue(null!=timeUnit,"timeUnit不能为空");
AssertLock.isTrue(0 < time,"time必须大于0");
AssertLock.isTrue(0 < num,"num必须大于0");
int flag = 0;
do {
boolean setFlag = redisTemplate.opsForValue().setIfAbsent(LockUtils.ttlLockKey(key,field),"lock");
if(setFlag) {
redisTemplate.expire(LockUtils.ttlLockKey(key,field),time,timeUnit);
return true;
}
Long ttlTime = redisTemplate.getExpire(LockUtils.ttlLockKey(key,field),timeUnit);
if(-1 == ttlTime){
redisTemplate.expire(LockUtils.ttlLockKey(key,field),time,timeUnit);
}
}while(flag++ < num);
return false;
}
/**
* 释放锁
* @param key
* @param field
* @return
*/
public void unLock(String key,String field){
AssertLock.isTrue(!StringUtils.isEmpty(key),"key不能为空");
AssertLock.isTrue(!StringUtils.isEmpty(field),"field不能为空");
redisTemplate.expire(LockUtils.ttlLockKey(key,field),0,TimeUnit.MILLISECONDS);
}
这里我们只是讨论了对于其中一种资源的锁,如果我们一次要操作多个资源呢?如果依次去获取,那么会导致死锁的发生。有兴趣的小伙伴可以思考一下如何同时处理集合中的多个资源。当然最简单的办法是采用粗粒度锁,每次都锁住整个集合