✅P162-163_缓存-分布式锁-Redisson-读写锁测试

gong_yz大约 3 分钟谷粒商城

开篇

参考文章:https://blog.csdn.net/Alvin199765/article/details/118163087open in new window

  • 读写锁通常都是成对出现的
  • 写锁控制了读锁
  • 只要写锁存在,读锁就得等待
  • 并发写,肯定得一个一个执行
  • 如果写锁不存在,读锁一直在那加锁,那跟没加是一样的

不加锁用例

示例代码

	//IndexController.java

	@Autowired
	StringRedisTemplate stringRedisTemplate;

	/**
     * 什么锁也不加,单纯的往redis中写入一个值
     *
     * @return
     */
    @ResponseBody
    @GetMapping("/write-unlock")
    public String writeUnlock() {
        String uuid = "";
        try {
            uuid = UUID.randomUUID().toString();
            TimeUnit.SECONDS.sleep(30);
            stringRedisTemplate.opsForValue().set("writeValue", uuid);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return uuid;
    }

    /**
     * 什么锁也不加,单纯的从redis中读取一个值
     *
     * @return
     */
    @ResponseBody
    @GetMapping("/read-unlock")
    public String readUnlock() {
        String name = "";
        name = stringRedisTemplate.opsForValue().get("writeValue");
        return name;
    }

加锁用例一

示例代码

//IndexController.java

	@Autowired
	RedissonClient redissonClient;
	@Autowired
	StringRedisTemplate stringRedisTemplate;

	@ResponseBody
    @GetMapping("/write")
    public String write(){
        RReadWriteLock lock = redissonClient.getReadWriteLock("rwAnyLock");
        String uuid = "";
        RLock rLock = lock.writeLock();
        try{
            rLock.lock();
            uuid = UUID.randomUUID().toString();
            TimeUnit.SECONDS.sleep(30);
            stringRedisTemplate.opsForValue().set("writeValue",uuid);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return uuid;
    }

    @ResponseBody
    @GetMapping("/read")
    public String read(){
        RReadWriteLock lock = redissonClient.getReadWriteLock("rwAnyLock");
        String uuid =  "";
        RLock rLock = lock.readLock();
        try{
            rLock.lock();
            uuid = stringRedisTemplate.opsForValue().get("writeValue");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return uuid;
    }

测试一

  • 先给redis中,手动添加一个writeValue的值,查看是否能读取到
  • 结果:可以

测试二

  • 先调用写的方法,再调用读的方法
  • 结果:读请求会一直加载,等写请求执行完业务之后,读请求瞬间加载到数据

加读写锁的作用就是,保证一定能读到最新数据。修改期间,写锁是一个互斥锁,读锁则是一个共享锁


加锁用例二

示例代码

//IndexController.java

	@Autowired
	RedissonClient redissonClient;
	@Autowired
	StringRedisTemplate stringRedisTemplate;

	@ResponseBody
    @GetMapping("/write")
    public String write() {
        RReadWriteLock lock = redissonClient.getReadWriteLock("rwAnyLock");
        String uuid = "";
        RLock rLock = lock.writeLock();
        try {
            rLock.lock();
            // 打印log
            System.out.println("写锁加锁成功" + Thread.currentThread().getId());
            uuid = UUID.randomUUID().toString();
            TimeUnit.SECONDS.sleep(30);
            stringRedisTemplate.opsForValue().set("writeValue", uuid);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
            // 打印log
            System.out.println("写锁释放" + Thread.currentThread().getId());
        }
        return uuid;
    }

    @ResponseBody
    @GetMapping("/read")
    public String read() {
        RReadWriteLock lock = redissonClient.getReadWriteLock("rwAnyLock");
        String uuid = "";
        RLock rLock = lock.readLock();
        try {
            rLock.lock();
            // 打印log
            System.out.println("读锁加锁成功" + Thread.currentThread().getId());
            uuid = stringRedisTemplate.opsForValue().get("writeValue");
            // 让读锁也等待30秒
            TimeUnit.SECONDS.sleep(30);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
            // 打印log
            System.out.println("读锁释放" + Thread.currentThread().getId());
        }
        return uuid;
    }

测试三

  • 先发送一个读请求,再发送一个写请求
  • 结果:读加上锁了,读释放锁之后,写才加上锁

测试四

  • 发送一个写请求,再发送四个读请求
  • 结果:写请求释放的瞬间,四个读请求都加上锁了

结论

读 + 读:相当于无锁,并发读,只会在redis中记录好当前的读锁,它们都会同时加锁成功;

写 + 读:读必须等待写锁释放;

写 + 写:阻塞方式;

读 + 写:有读锁,写也需要等待;

只要有一个写存在,其它的读/写就必须等待