1、一个tomcat是一个进程,其中有很多线程(与有多少个app无关)
2、一个tomcat启动一个JVM,其中可以有很多APP
3、一个tomcat中部署的多个app,虽然同处一个JVM里,但是由于无法相互调用,所以也可以认为是分布式的
synchronized 只是本地锁啊,锁的也只是当前jvm下的对象,在分布式场景下,要用分布式锁。
redis 分布式锁应用场景: 程序不是在一台tomcat(不同jvm)或者一台 tomcat部署的多个由于无法相互调用,synchronized失效,此时操作共享变量,例如库存,就要用分布式锁
简陋版:
@RequestMapping("/deduct_stock")
public String deductStockO throws InterruptedExceptiQn {
String lockKey = "lockKey":
//Boolean result = stringRedisTemplate. opsForValue().set If Absent (lockKey, "lyy"); //jedis. setnx (key, value);
//stringRedisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);
String uuid = UUID.randomUUID().toString();
//一行操作保证原子性
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value: uuid ,timeout: 10, TimeUnit.SECOSDS);
if (!result ) {
//lockKey 被占用 操作视情况
return "error";
}
try {
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get ("stock")); // jedis.get ("stock")
if (stock > 0){
int realStock = stock - 1:
stringRedisTemplate. opsForValueO, setCstock", realStock + ""):// jedis.set (key, value)
System.out.println("扣减成功,剩余库存:"+ realStock):
} else {
System.out.println("扣减失敗,库存不足");
}
} finally {
//避免锁永久失效(线程a执行时间大于lockKey的失效时间,导致线程一未执行完线程二进入,生成新的锁,线程一执行完后删除线程二的锁,然后线程三进来。。。导致锁失效)
if(uuid.equals(stringRedisTemplate.opsForValue().get(lockKey))){
stringRedisTemplate.delete(lockKey);
}
}
return end;
}
解决key 失效时间小于业务执行时间问题
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
//放到启动类
@Bean
public Redisson redisson(){
//此为单机模式
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);
/*集群模式
config.useClusterServers()
.addNodeAddress("redis://192.168.0.61:8001")
.addNodeAddress("redis://192.168.0.61:8001")
.addNodeAddress("redis://192.168.0.61:8001")
.addNodeAddress("redis://192.168.0.61:8001")
.addNodeAddress("redis://192.168.0.61:8001");*/
return (Redisson) Redisson.create(config);
}
redisson底层主要是lua脚本
原理图:
解决key 失效时间小于业务执行时间问题
使用lua后的效果:
@Autowired
Redisson redisson;
@RequestMapping("/deduct_stock")
public String deductStockO throws InterruptedExceptiQn {
String lockKey = "lockKey":
//Boolean result = stringRedisTemplate. opsForValue().set If Absent (lockKey, "lyy"); //jedis. setnx (key, value);
//stringRedisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);
RLock redissonLock = redisson.getLock(lockKey);
//String uuid = UUID.randomUUID().toString();
//一行操作保证原子性
//Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value: uuid ,timeout: 10, TimeUnit.SECOSDS);
// if (!result ) {
//lockKey 被占用 操作视情况
// return "error";
//}
try {
//加锁
redissonLock.lock();
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get ("stock")); // jedis.get ("stock")
if (stock > 0){
int realStock = stock - 1:
stringRedisTemplate. opsForValueO, setCstock", realStock + ""):// jedis.set (key, value)
System.out.println("扣减成功,剩余库存:"+ realStock):
} else {
System.out.println("扣减失敗,库存不足");
}
} finally {
//释放锁
redissonLock.unlink();
//避免锁永久失效(线程a执行时间大于lockKey的失效时间,导致线程一未执行完线程二进入,生成新的锁,线程一执行完后删除线程二的锁,然后线程三进来。。。导致锁失效)
/*if(uuid.equals(stringRedisTemplate.opsForValue().get(lockKey))){
stringRedisTemplate.delete(lockKey);
}*/
}
return end;
}
redis 集群,主redis挂了,此时还没同步到从redis,怎么办?
可以使用zookeeper,它会等 其他的zookeeper同步加速成功再返回成功
redis没办法100%解决这个问题,可以容忍,redis性能远高于zookeeper
解决
1.可以使用redlock(不推荐,不完善):2.使用redission
@RequestMapping("/redlack")
public String redlock() throws InterruntedExcention (
String lock Key=”product_001”;
//这里需要自己实例化不同red is实例的redisson客户端连接, 这里只是伪代码用一个red is son客户端简化了
RLock lock1 = redisson.get Lock(lockKey);
RLock lock2 = redisson.get Lock(lockKey);
RLock lock3 = redisson.get Lock(lockKey);
//无论如何,最后都要解锁RedissouRedLock(最核心的差别就在这里)
Redisson RedLockred Lock=new Redisson RedLock(lock1,lock2,lock3);
try (
//waitTimeout尝试获取锁的最大等待时间, 超过这个值, 则认为获取锁失败
//leaseTime 锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完)
boolean res=redLock.tryLock(waitTime:10,leaseTime:30, TimeUnit.SECONDS);
if (res) (
//成功获得锁,在这里处理业务
) catch(Exception e) (
throw new RuntimeException(”lock fail);
) finally{
//无论如何,最后都要解锁
redissonLock.unlink();
}
return end;
}
高并发分布式锁实现:
将数据在redis里分段减库存