一、比较
性能:DB锁>Redis锁
实现难度:DB锁<Redis锁
灵活应用:Redis锁>DB锁
业务复杂度:DB锁>Redis锁
二、实现
1、DB锁
db锁的实现比较简单,通过 select * from table where id=? for update 进行行级锁。
直接上代码
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)//一定要开启事务,才能用锁哦!!!
public Do lockDbTest(Long id) {
Do do=DoDao.lockById(id);
//业务实现.....
return do;
}
2、Redis锁
redis锁实现思路:原子性操作redis。并且加上过期时间,避免服务器崩溃等原因,造成死锁。
代码:
public boolean tryLock(RedisLock redisLock) {
int i = 0;
while (i < 3) {
if (stringRedisTemplate.opsForValue().setIfAbsent(redisLock.getLockKey(), redisLock.getLockValue(), 30, TimeUnit.SECONDS)) {
// System.out.println(Thread.currentThread().getId() + "加锁成功! key:"+redisLock.getLockKey()+"value:"+redisLock.getLockValue());
//开启定时刷新过期时间
redisLock.isOpenExpirationRenewal = true;
redisLock.scheduleExpirationRenewal(stringRedisTemplate);
return true;
}
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// System.out.println(Thread.currentThread().getId() + "加锁失败!");
return false;
}
由于有了过期时间,所以要加上刷新过期时间控制。
代码:
public RedisLock(String lockKey) {
this.lockKey = lockKey;
this.lockValue = UUID.randomUUID().toString();
}
private String lockKey;
private String lockValue;
protected volatile boolean isOpenExpirationRenewal = true;
private StringRedisTemplate stringRedisTemplate;
public String getLockKey() {
return lockKey;
}
public String getLockValue() {
return lockValue;
}
public void setLockValue(String lockValue) {
this.lockValue = lockValue;
}
/**
* 开启定时刷新
*/
protected void scheduleExpirationRenewal(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
Thread renewalThread = new Thread(new ExpirationRenewal());
renewalThread.start();
}
public void sleepBySencond(int sencond) {
try {
Thread.sleep(sencond * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 刷新key的过期时间
*/
private class ExpirationRenewal implements Runnable {
@Override
public void run() {
while (isOpenExpirationRenewal) {
//休眠10秒
System.out.println("执行延迟失效时间中..."+Thread.currentThread().getId());
stringRedisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
sleepBySencond(10);
}
}
}
最后来一个释放锁
直接代码上:
public boolean releaseLock(RedisLock redisLock) {
// 错误示范哦 stringRedisTemplate.delete(redisLock.getLockKey());
// redisLock.isOpenExpirationRenewal = false;
redisLock.isOpenExpirationRenewal = false;
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(RELEASE_LOCK_LUA_SCRIPT);
redisScript.setResultType(Long.class);
Long result=stringRedisTemplate.execute(redisScript, Collections.singletonList(redisLock.getLockKey()), redisLock.getLockValue());
if(result==RELEASE_LOCK_SUCCESS_RESULT) {
// System.out.println("解锁成功:key:"+redisLock.getLockKey()+"value:"+redisLock.getLockValue());
return true;
}
// System.out.println("失败解锁结果:key:"+redisLock.getLockKey()+"value:"+redisLock.getLockValue());
// System.out.println("失败解锁锁值:"+stringRedisTemplate.opsForValue().get(redisLock.getLockKey()));
return false;
}
需要解释一下的是,redisLock既然有key了,为啥还要value呢,这是由于避免非本客户端来解锁哦。
其中还有需要注意的spring-boot版本呀等问题。很多细节就不说了....大家有兴趣慢慢研究~~~~~