springboot--redis解决分布式锁--使用watchDog机制解决超时引起的线程安全问题

485 阅读2分钟

锁: 保证程序的原子性操作。

我们在单台服务器运行多线程的时候,会出现重买超卖的情况,这个时候可以通过加锁来保证代码的原子性操作,但是当我们多台服务器对应一个数据库的时候,依旧会出现线程安全问题,这个时候该怎么解决

redis解决分布式锁

image.png

实现代码

@Service
public class ProductStockServiceImpl2 implements ProductStockService {
    @Autowired
    private ProductStockDao productStockDao;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public  String decreaseStock(Integer productId) {
        final ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        Boolean cjj = forValue.setIfAbsent("product:" + productId, "cjj", 30, TimeUnit.SECONDS);//todo 获得锁,如果为true代表获取锁成功
        if(cjj){//todo 如果获取的返回值为true,则表示获取分布锁成功
            try{
                //查看该商品的库存数量
                Integer stock = productStockDao.findStockByProductId(productId);
                if (stock > 0) {
                    //修改库存每次-1
                    productStockDao.updateStockByProductId(productId);
                    System.out.println("扣减成功!剩余库存数:" + (stock - 1));
                    return "success";
                } else {
                    System.out.println("扣减失败!库存不足!");
                    return "fail";
                }
            }finally {
                stringRedisTemplate.delete("product:"+productId);//todo 删除key表示释放锁资源
            }

        }else{
            try {
                Thread.sleep(200);//休息200毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return decreaseStock(productId);
        }

    }

测试结果

image.png

缺陷: 当程序执行时间超过锁的时间。还是会发生线程安全问题

解决: 使用watch dog机制。----第三方框架完成redisson

使用watch dog机制 解决当程序执行时间超过锁的时间导致的线程安全问题

blog.csdn.net/guntun8987/…

github.com/redisson/re…

(1)引入redissson依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.4</version>
</dependency>

(2)自己创建redisson对象并交予springioc容器管理

@Bean
public RedissonClient redissonClient(){
    Config config = new Config();
    config.useSingleServer()
            // use "rediss://" for SSL connection
            .setAddress("redis://192.168.94.131:6379");
    RedissonClient redissonClient = Redisson.create(config);
    return redissonClient;
}

(3)在service中使用

@Autowired
private ProductStockDao productStockDao;
@Autowired
private RedissonClient redissonClient;//redisson锁对象,用来解决程序执行超时问题和分布式线程安全问题

@Override
public  String decreaseStock(Integer productId) {
    RLock lock = redissonClient.getLock("product:"+productId);
    try{
        lock.lock(30,TimeUnit.SECONDS); //这里的锁时间必须是30s
            //查看该商品的库存数量
            Integer stock = productStockDao.findStockByProductId(productId);
            if (stock > 0) {
                //修改库存每次-1
                productStockDao.updateStockByProductId(productId);
                System.out.println("扣减成功!剩余库存数:" + (stock - 1));
                return "success";
            } else {
                System.out.println("扣减失败!库存不足!");
                return "fail";
            }
        }finally {
            lock.unlock();//释放锁
        }
}

(4)使用JMeter压测

image.png