/**
*
* @author IceYan
* @since 2021-10-01
*/
@Service
public class BaseBrandServiceImpl extends ServiceImpl<BaseBrandMapper, BaseBrand> implements BaseBrandService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
public void setNum0() {
String value = (String)redisTemplate.opsForValue().get("num");
if(StringUtils.isEmpty(value)){
redisTemplate.opsForValue().set("num","1");
}else{
int num = Integer.parseInt(value);
//对num加1操作然后放入redis当中
redisTemplate.opsForValue().set("num",String.valueOf(++num));
}
}
//@Override
public synchronized void setNum01() {
String value = (String)redisTemplate.opsForValue().get("num");
if(StringUtils.isEmpty(value)){
redisTemplate.opsForValue().set("num","1");
}else{
int num = Integer.parseInt(value);
//对num加1操作然后放入redis当中
redisTemplate.opsForValue().set("num",String.valueOf(++num));
}
}
private void doBusiness() {
String value = (String) redisTemplate.opsForValue().get("num");
if (StringUtils.isEmpty(value)) {
redisTemplate.opsForValue().set("num", "1");
} else {
int num = Integer.parseInt(value);
//对num加1操作然后放入redis当中
redisTemplate.opsForValue().set("num", String.valueOf(++num));
}
}
//分布式锁 方案一 缺点:if逻辑中若抛了异常,无法释放锁
public void setNum1() {
//如果往redis里面设置值成功代表获取到了锁
boolean acquireLock = redisTemplate.opsForValue().setIfAbsent("lock", "ok");
if(acquireLock){
//如果这里出现异常 锁无法释放 会一直占用
doBusiness();
//★执行完业务逻辑之后删除锁,否则其他线程拿不到释放后的锁了
redisTemplate.delete("lock");
}else{
//设置睡眠时间
SleepUtils.sleepMillis(50);
//自旋
setNum();
}
}
//分布式锁 方案二 缺点:锁过期时间和业务处理时间不一致,导致删除其他线程的锁
public void setNum2() {
//如果往redis里面设置值成功代表获取到了锁 到期之后自动释放锁 但是此时有可能会把其他线程的锁给删掉
boolean acquireLock = redisTemplate.opsForValue().setIfAbsent("lock", "ok",3, TimeUnit.SECONDS);
if(acquireLock){
doBusiness();
//执行完业务逻辑之后删除锁
redisTemplate.delete("lock");
}else{
//设置睡眠时间
SleepUtils.sleepMillis(50);
//自旋
setNum();
}
}
//分布式锁 方案三 缺点:删除锁的操作不具备原子性,不保证判断和删除所操作同时执行成功/失败(原子性)
public void setNum3() {
//加锁的值信息
String uuid = UUID.randomUUID().toString();
//如果往redis里面设置值成功代表获取到了锁
boolean acquireLock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
if(acquireLock){
doBusiness();
//删除之前判定是否为该线程的锁 存在的问题在于下面的判断和删除不具备原子性
String redisUuid = (String)redisTemplate.opsForValue().get("lock");
if(uuid.equals(redisUuid)){
//执行完业务逻辑之后删除锁
redisTemplate.delete("lock");
}
}else{
//设置睡眠时间
SleepUtils.sleepMillis(50);
//自旋
setNum();
}
}
//分布式锁 方案四 lua 缺点:集群下依旧有安全问题,拿不同线程的锁
public void setNum4() {
//加锁的值信息
String uuid = UUID.randomUUID().toString();
//如果往redis里面设置值成功代表获取到了锁
boolean acquireLock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
if(acquireLock){
doBusiness();
//定义一个lua脚本
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
//把上面的script代码放到redisScript对象中
redisScript.setScriptText(script);
//设置执行脚本之后返回值是什么类型
redisScript.setResultType(Long.class);
//准备执行lua脚本
redisTemplate.execute(redisScript, Arrays.asList("lock"),uuid);
/**
* 上面的一大坨 就是下面两句话 把它原子性了
* //删除之前判定是否为该线程的锁
* String redisUuid = (String)redisTemplate.opsForValue().get("lock");
* if(uuid.equals(redisUuid)){
* redisTemplate.delete("lock");
* }
*/
}else{
//设置睡眠时间
SleepUtils.sleepMillis(50);
//自旋
setNum();
}
}
//redissonClient方式一
//@Override
public void setNum11() {
RLock lock = redissonClient.getLock("lock");
lock.lock();
doBusiness();
lock.unlock();
}
//redissonClient方式二
//@Override
public void setNum22() {
RLock lock = redissonClient.getLock("lock");
//加锁以后3秒之后自动释放 无需调用unlock
lock.lock(3, TimeUnit.SECONDS);
doBusiness();
lock.unlock();
}
//★★★最终方案:redissonClient方式三
@Override
public void setNum() {
RLock lock = redissonClient.getLock("lock");
try {
/**
* 参数1.尝试获得锁的最大等待时间 超过这个时间没有获得锁 代表取得锁失败
* 参数2.锁的过期时间(锁的过期时间应该大于业务处理时间 确保业务能在锁有效期内处理完)
*/
boolean acquireLock = lock.tryLock(100, 3, TimeUnit.SECONDS);
if(acquireLock){
doBusiness();
}else{
//设置睡眠时间
SleepUtils.sleepMillis(50);
//自旋
setNum();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
★★稳定、靠谱的分布式锁的刚需条件:
1.独占性
任何时刻,有且仅有一个线程持有。
2.高可用(多机)
redis集群环境下,不能因为某个节点挂了而出现获取锁和释放锁失败的情况。
3.防死锁
必须有超时控制/撤销操作,有个兜底终止跳出方案。
4.不乱抢
不能解开别人的锁,只能操作自己的锁
5.重入性
参考可重入锁
redis单机:p; redis集群:ap(高性能高并发) ;
zookeeper集群:cp(数据一致性)
即使用lua也有缺陷的原因:
集群下,主节点挂掉,它的分布式锁没来得及同步给从节点。