redisson 分布式锁(二)

265 阅读3分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情

尝试锁 tryLock

boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
//加锁成功
if (res) {
   try {
     //执行业务逻辑
   } finally {
       //执行解锁
       lock.unlock();
   }
}

尝试加锁,最多等待100秒,上锁以后10秒自动解锁,这个尝试枷锁有点像基础的限流方式,等待的100秒时间就像是队列,可以达到限流的效果。100秒之后灭有获取到锁就直接返回我我们的定义的结果就行。
当然tryLock也有异步执行的方法

RLock lock = redisson.getLock("asyncLock");
lock.lockAsync();// 注意这里也是
lock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = lock.tryLockAsync(100, 10, TimeUnit.SECONDS);//异步编排的方式获取结果

可以点进去看到这个异步方式调用 RFuture 继承的是Future和CompletionStage 接口

public interface RFuture<V> extends Future<V>, CompletionStage<V> {}

lockAsync 锁方法点进去可以看到源码的实现代码也是先去检查所是不是在被占用,如果占用中就返回失败相反加锁成功。(感兴趣的可以点进去源码里面看看) image.png

@PostMapping("/test")
public R test(@RequestBody ListVo listVo) throws InterruptedException {
    RLock lock = redissonClient.getLock("anyLock");
    RFuture<Void> voidRFuture = lock.lockAsync();
    voidRFuture.onComplete((res,e)->{
        System.out.println("枷锁结果 " + voidRFuture.isSuccess());
        System.out.println("res 子线程枷锁 线程号: " + Thread.currentThread().getId());
    });
    System.out.println("res 主线程继续执行: " + Thread.currentThread().getId());
    try {
        Thread.sleep(20);
    }catch (Exception exception){
        System.out.println("exception = " + exception);
    }
    lock.unlock();
    return  R.ok();
}

可以看到 异步加锁并不会影响到主线程的进程。

res 主线程继续执行: 83
枷锁结果 true
res 子线程枷锁 线程号: 55

读写锁ReadWriteLock

保持数据一致性写锁控制读锁,并发读是没有问题的,但是并发写只能一个一个排队且这时的读锁也是必须等待写锁完成释放之后才能获取到。(读锁和写锁是成对出现的)

@PostMapping("/write")
public R write(@RequestBody ListVo listVo){
    RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
    //加写锁
    RLock rLock = readWriteLock.writeLock();
    String uuid= UUID.randomUUID().toString();
    try {
        rLock.lock();
        System.out.println("写 加锁成功 = " + Thread.currentThread().getId());
        Thread.sleep(30000);//注意这里添加睡眠是为了更好的看到效果
        redisTemplate.setCacheObject("write",uuid);
    }catch (Exception exception){
        System.out.println("exception = " + exception);
    }finally {
        System.out.println("写 释放锁成功 = " + Thread.currentThread().getId());
        rLock.unlock();
    }
   
    return  R.ok();
}
@PostMapping("/read")
public R read(@RequestBody ListVo listVo) {
    RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
    //加读锁
    RLock rLock = readWriteLock.readLock();
    try {
    rLock.lock();
    String uuid= UUID.randomUUID().toString();
    System.out.println("读 加锁成功 = " + Thread.currentThread().getId());
    redisTemplate.getCacheObject("write");
    }catch (Exception e){
        
    }finally {
        rLock.unlock();//解锁
        System.out.println("读 释放锁成功 = " + Thread.currentThread().getId());
    }
    return  R.ok();
}

保证一定能读到最新的数据,修改同步数据期间写锁是一个排他锁(互斥锁、独享锁)读锁是一个共享锁,写锁没释放读锁就必须一致等待

image.png 在写锁更新是读是要等待完成之后才能获取到最新的数据(也就是阻塞)。

读 + 读 :相当于无所,并发读,只会在redis中记录,所有当前的读锁都会同时加锁成功 image.png 写 + 读 :等待写锁释放(阻塞等待)

image.png 写 + 写 :等待上一个写锁释放(阻塞等待)

image.png 读 + 写 :写锁要等待读锁释放才可以

image.png 可以看出来只要有写锁存在都必须等待。
实践是检验真理的唯一准则,感兴趣的可以去试试呀!明天见咯 😃😃😃😃