目录
一、本地锁
二、分布式锁
①什么是分布式锁?
②为什么需要分布式锁?
分布式项目时,但本地锁只能锁住当前服务,需要分布式锁
③分布式锁引发死锁问题
死锁的几种方式
①设置锁之后,删除锁之前断电了(在加锁的时候也设置过期时间,也就是加锁的时候设置成原子性的)
②业务的执行时间比较长,在这个时间中锁已经过期了,这时就删除了别人的锁(设置一个随机值,然后进行比较是否是当前的这个锁)
③业务执行时间比较长,比较随机值之后确定是当前锁,这时锁已经过期了,别人占了这个锁,所以我们删的就是别人的锁
④假设现在一个用户A访问表A(锁住了表A),然后试图访问表B; 另一个用户B访问表B(锁住了表B),然后试图访问表A。
三、Redisson完成分布式锁
①Redisson是什么?
Redisson 是架设在 Redis 基础上的一个 Java 驻内存数据网格(In-Memory Data Grid)。充分的利用了 Redis 键值数据库提供的一系列优势,基于 Java 实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。
②我们为什么需要Redisson?
①无论业务有多长,只要没有执行完,redission都会自动续约增加至30s的时间(看门狗)
②只要业务代码执行完,redission就不会自动续约,30s之后会自动解锁
③如何使用Redssion?
①引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
②把配置类加入spring容器
@Configuration
public class MyRedisConfig {
@Value("${ipAddr}")
private String ipAddr;
// redission通过redissonClient对象使用 // 如果是多个redis集群,可以配置
@Bean(destroyMethod = "shutdown")
public RedissonClient redisson() {
Config config = new Config();
// 创建单例模式的配置
config.useSingleServer().setAddress("redis://" + ipAddr + ":6379");
return Redisson.create(config);
}
}
③在spring.properties写redis地址信息
ipAddr=192.168.80.128
④测试
@Autowired
private RedissonClient redissonClient;
@GetMapping("/hello")
public String helloLock(){
RLock lock = redissonClient.getLock("category-xjh");
lock.lock();//阻塞等待,默认加的锁时间是30s
// 解锁放到finally // 如果这里宕机:有看门狗,不用担心
try {
System.out.println("加锁成功,执行业务"+Thread.currentThread().getId());
Thread.sleep(3000);
}finally {
System.out.println("解锁成功"+Thread.currentThread().getId());
lock.unlock();
return "hello";
}
}
④Redisson读写锁的测试
@GetMapping("/writeLock")
public String writeLock() throws InterruptedException {
RReadWriteLock writeLock = redissonClient.getReadWriteLock("rwLock");
try {
writeLock.writeLock().lock();
Thread.sleep(3000);
stringRedisTemplate.opsForValue().set("write","123");
} finally {
writeLock.writeLock().unlock();
return "写锁成功";
}
}
@GetMapping("/readLock")
public String readLock() throws InterruptedException {
RReadWriteLock writeLock = redissonClient.getReadWriteLock("rwLock");
writeLock.writeLock().lock();
stringRedisTemplate.opsForValue().get("write");
writeLock.writeLock().unlock();
return "读数据成功";
}
⑤几种读写锁情况
//写锁没有释放,读锁就必须要等待
①读+读 相当于没有锁,他们都会同时加锁
②写+读 只有写锁执行完之后读锁才会去读取数据
③写+写 阻塞
④读+写 只有等待读锁执行完成之后写锁才会去执行
⑤Redission的闭锁的测试
// 闭锁的例子(每个班都放假了,然后就全体放假)
@GetMapping("/downLock")
public String closeLock() throws InterruptedException {
RCountDownLatch downLock = redissonClient.getCountDownLatch("downLock");
try {
downLock.trySetCount(3);
downLock.await();
} finally {
return "放假啦";
}
}
@GetMapping("/goHome/{id}")
public String downLock(@PathVariable String id) {
RCountDownLatch downLock = redissonClient.getCountDownLatch("downLock");
downLock.countDown();
return id + "跑路了";
}
⑥Redission的信号量测试
// 量子化的操作(停车)
@GetMapping("parkCar")
public String parkCar() {
RSemaphore parkCar = redissonClient.getSemaphore("parkCar");
boolean b = parkCar.tryAcquire();
return "车停进来了"+b;
}
@GetMapping("goCar")
public String goCar() {
RSemaphore goCar = redissonClient.getSemaphore("parkCar");
goCar.release();
return "车开出去了";
}