reentrantLock的使用

reentrantLock是和锁有关,那么必然就涉及到并发操作,所以我们先构建一个项目:

依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

准备工作

往redis里面放一个值,num,值设置成100

相关代码

package com.jiubodou.bingfa.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassName MyController
 * @Description
 * @Author huyingliang
 * @Date 2023/5/23 15:32
 */
@RestController
public class MyController {

    public static ReentrantLock reentrantLock = new ReentrantLock();

    @GetMapping("/")
    public String sayHello() {
        return "Hello,World!";
    }

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/redis")
    public String redis() {
        stringRedisTemplate.opsForValue().set("num", "1");
        return stringRedisTemplate.opsForValue().get("num");
    }

    @GetMapping("/addNum")
    public String addNum() {
        String s = stringRedisTemplate.opsForValue().get("num");
        int num = Integer.parseInt(s) + 1;
        stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
        return stringRedisTemplate.opsForValue().get("num");
    }

    @GetMapping("/minusNum")
    public String minusNum() {

        reentrantLock.lock();
        try {
            String s = stringRedisTemplate.opsForValue().get("num");
            int num = Integer.parseInt(s) - 1;
            stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
            return stringRedisTemplate.opsForValue().get("num");
        } finally {
            reentrantLock.unlock();
        }

    }

    @GetMapping("/addNumAuto")
    public String addNumAuto() {
        Long num = stringRedisTemplate.opsForValue().increment("num",1);
        return String.valueOf(num);
    }

    @GetMapping("/minusNumAuto")
    public String minusNumAuto() {
        Long num = stringRedisTemplate.opsForValue().increment("num", -1);
        return String.valueOf(num);
    }
}

我们看第一个接口 addNum

    @GetMapping("/addNum")
    public String addNum() {
        String s = stringRedisTemplate.opsForValue().get("num");
        int num = Integer.parseInt(s) + 1;
        stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
        return stringRedisTemplate.opsForValue().get("num");
    }

很简单,就是将redis里面的值拿出来以后,加一再放回去,但是如果是并发场景下呢?
我们这里使用 ApiFox来测试一下
在这里插入图片描述
8个现成循环5次,也就是说,最终redis里面值应该是 100+40=140,但是结果是130
在这里插入图片描述
同样的,如果不加锁的情况下去执行 minusNum,

    @GetMapping("/minusNum")
    public String minusNum() {

            String s = stringRedisTemplate.opsForValue().get("num");
            int num = Integer.parseInt(s) - 1;
            stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
            return stringRedisTemplate.opsForValue().get("num");

    }

执行结果也肯定不会是 130-40=90。那么解决方案其中一个就是加锁解决这个问题

    @GetMapping("/minusNum")
    public String minusNum() {

        reentrantLock.lock();
        try {
            String s = stringRedisTemplate.opsForValue().get("num");
            int num = Integer.parseInt(s) - 1;
            stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
            return stringRedisTemplate.opsForValue().get("num");
        } finally {
            reentrantLock.unlock();
        }

    }

那么有没有其他方法来解决这个并发问题呢,其实是有的,我们使用redis,其中有一个操作就是 原子性的增加/减

    @GetMapping("/addNumAuto")
    public String addNumAuto() {
        Long num = stringRedisTemplate.opsForValue().increment("num",1);
        return String.valueOf(num);
    }

    @GetMapping("/minusNumAuto")
    public String minusNumAuto() {
        Long num = stringRedisTemplate.opsForValue().increment("num", -1);
        return String.valueOf(num);
    }

这样也可以保证并发情况下的数据一致性