-
本地锁 只能锁定自己的服务
-
SET key value [EX seconds] [PX milliseconds] [NX|XX] http://www.redis.cn/commands/set.html
public Map<String, List<Catelog2Vo>> getCatalogJsonWithRedisLock() {
//redis 分布原则 加锁保证原子性,解锁保证原子性
//1.占用分布式锁 去redis站坑setIfAbsent = SET NX,随便set K V数值,之后返回一个结果 =>2.设置过期时间和枷锁是同步的原子的
String uuid = UUID.randomUUID().toString(); //设置v值是uuid
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
if (lock) {
System.out.println("获取分布式锁成功...");
//1.1加锁成功 执行业务
//2.设置期时间 以免造成死锁 设置 30秒自动删除 ;设置过期时间和枷锁是同步的原子的
Map<String, List<Catelog2Vo>> dataFromDb;
try {
//redisTemplate.expire("lock",30,TimeUnit.SECONDS);
dataFromDb = getDataFromDb();
} finally {
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
Long lock1 = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
}
//3.删除 不是自己锁 获取数值对比+对比删除成功=原子操作 lua 解锁操作
/**
* String lockValue = redisTemplate.opsForValue().get("lock"); //查到锁的数值
* //2.1查看放的uuid和lockValue一致 说明 锁是自己加的 在执行删除自己 锁
* if (uuid.equals(lockValue)){
* //1.2执行成功之后 解锁/删除锁 共其他使用
* redisTemplate.delete("lock");
* }
*/
return dataFromDb;
} else {
//枷锁失败。。在重置,把自己在调一遍
try {
Thread.sleep(200);
}catch (Exception e){
}
System.out.println("获取分布式锁失败 重试...");
return getCatalogJsonWithRedisLock();//自旋方式
}
}
导入pom
<!--用redisson作为分布式锁,分布式对象 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
配置MyRedissonConfig
@Configuration
public class MyRedissonConfig {
/**
* 对于redisson配置是RedissonClient操作的
*
* @return
* @throws IOException
*/
@Bean(destroyMethod = "shutdown")
RedissonClient redisson() throws IOException {
//创建配置
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.56.10:6379");
//config.useClusterServers() 集群模式
// .addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
//依据Config创建RedissonClient
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
测试加锁 解锁
public String hello() {
//1.获取一把锁 只要锁 名字一样就是用一把锁
RLock lock = redisson.getLock("my_lock");
//2.加锁
lock.lock(30, TimeUnit.SECONDS);
//lock.lock(); //堵塞式等待
try {
//打印线程好 如果服务中断 有个看门狗自动续期
System.out.println("加锁成功执行业务"+Thread.currentThread().getId());
Thread.sleep(30000);
} catch (InterruptedException e) {
} finally {
//3.解锁
System.out.println("解锁成功执行业务"+Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
//2.加锁 锁的自动续期,如果业务超长,运行期间自动给锁续上行的时间,不用担心业务时间长 锁自动过期被删掉 // 加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后自动删除。
- 10秒自动解锁,自动解锁时间-定要大于业务的执行时间。在锁时间到了以后,不会自动续期。
- 如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
- 如果我们未指定锁的超时时间,就使用30 * 1000 [LockWatchdogTimeout看门狗的默认时间] ;
- 只要占锁成功,就会启动一个定时任务[重新给锁设置过期时间,新的过期时间就是看[ ]狗的默认时间]
- internallockLeaseTime [看门狗时间] / 3, 10s 规则用 lock.lock(30, TimeUnit.SECONDS); 加锁时间>=解锁时间
读写锁测试
- 保证一 定能读到最新数据,修改期间,写锁是一 个排他锁(互斥锁,独享锁)。读锁是一 一个共享锁
- 与锁役释放读就必须寺待
- 读+读 相当于无锁,并发读,只会在redis中记录好,所有当前的读锁。他们都会同时加锁成功
- 写+读:等待写锁释放
- 写+写:阻塞方式
- 读+写:有读锁。写也需要等待。
- 只要有写的存在,都必须等待
@ResponseBody
@GetMapping("/write") //读写锁
public String writeValue() {
String s = "";
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");//加写锁
RLock rLock = lock.writeLock();
try {
//改数据加写锁,读数据加读锁
rLock.lock();
s = UUID.randomUUID().toString();
redisTemplate.opsForValue().set("writeValue", s);
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rLock.unlock();
}
return s;
}
@ResponseBody
@GetMapping("/read") //读写锁
public String readValue() {
String s = "";
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
RLock rLock = lock.readLock(); //加读锁
//执行业务代码
rLock.lock();
try {
s = redisTemplate.opsForValue().get("writeValue");
} catch (Exception e) {
e.printStackTrace();
} finally {
rLock.unlock();
}
return s;
}
Redisson-信号量测试
车库停车 信号量测试 限流使用
@GetMapping("park")
@ResponseBody
public String park() throws InterruptedException {
//使用信号量命名
RSemaphore park = redisson.getSemaphore("park");
//获取一个信号 获取一个数值 占一个车位 获取失败会一直等待
boolean b = park.tryAcquire(); //尝试获取 能否取到数值 不行就false 走
if (b){
//执行代码
}else {
return "error";
}
park.acquire();
return "ok停车+1";
}
@GetMapping("go")
@ResponseBody
public String go() throws InterruptedException {
//使用信号量命名
RSemaphore park = redisson.getSemaphore("park");
//释放信号
park.release();
return "ok 开走-1";
}
Redisson-闭锁测试
分布式闭锁 放假锁门 等到全部走完 才关锁
@GetMapping("/lockDoor")
@ResponseBody
public String lockDoor() throws InterruptedException {
RCountDownLatch doot = redisson.getCountDownLatch("door");
doot.trySetCount(5);//等待5个
doot.await();//等待闭锁都完成
return "放假了";
}
@GetMapping("/gogo/{id}")//接收走的id
@ResponseBody
public String gogo(@PathVariable("id") Long id){
RCountDownLatch doot = redisson.getCountDownLatch("door");
doot.countDown();//等价于计数减一
return id+"走了";
}
修正之前方法
public Map<String, List<Catelog2Vo>> getCatalogJsonWithRedissonLock() {
//去占锁
RLock lock = redisson.getLock("catalogJson-lock");
//占锁之后 加锁
lock.lock();
Map<String, List<Catelog2Vo>> dataFromDb;
try {
dataFromDb = getDataFromDb();
} finally {
//解锁
lock.unlock();
}
return dataFromDb;
}
缓存-分布式锁-缓存一致性解决