分布式锁
一、用压测模拟并发
使用简易工具Apache ab
ab -n 100 -c 100 http://www.baidu.com/
表示: -n 表示发出100个请求,-c表示100的并发
ab -t 60 -c 100 http://www.baidu.com/
表示: -t 表示时间60 s
二、高并发下的线程安全–Redis实现分布式锁
这里使用到redis的命令setnx()和getset()命令。
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 加锁
* @param key
* @param value:当前时间+超时时间
* @return
*/
public boolean lock(String key,String value){
//某个线程拿到锁,setIfAbsent就是setnx,即之前没有线程对该key执行过set操作,那么就能拿到锁,若之前有线程对该key执行过set操作
//那么返回false,即拿不到锁
if (stringRedisTemplate.opsForValue().setIfAbsent(key,value)) {//1
return true;
}
/**
* 此处代码的作用是防止死锁的出现:当在加锁和解锁之间发生某些错误,导致代码无法执行到解锁的代码,就会发生死锁。
* 那么,该段代码是如何防止死锁的呢?假设有一个线程a
发生了死锁其value为A,且已经超时。那么有两个线程b,c进来其value为B,代码1的条件不能满足,那么线程b,c
* 来到代码2,他们都能获取到死锁线程a的value值为currentValue。且都能执行到3,都满足条件(因为已经超时了)。现在假设有一个线
程,假设是b线程先于c线程执行了代码4.
* 此时执行getAndSet成功,线程b的oldValue=currentValue=A,设置成功的value=B,,此时满足5的条件,获得锁(死锁在此解开);当线
程c执行代码4时,执行getAndSet,get到的oldValue=B
* 当线程c执行到5时不满足5的条件,因此无法获取到锁。
*/
String currentValue = stringRedisTemplate.opsForValue().get(key);//2
//如果多过期
if (!StringUtils.isEmpty(currentValue)
&&Long.parseLong(currentValue)<System.currentTimeMillis()) {//3
String oldValue = stringRedisTemplate.opsForValue().getAndSet(key,value);//4
if (!StringUtils.isEmpty(oldValue)&&oldValue.equalsIgnoreCase(currentValue)) {//5
return true;
}
}
//线程没有拿到锁
return false;
}
/**
* 解锁
* @param key
* @param value
*/
public void unlock(String key,String value) {
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue)&¤tValue.equals(value)) {
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("【redis分布式锁】解锁异常,{}",e);
}
}
}