SpringBoot中使用Redis作为全局锁示例过程

目录
  • 一、模拟没有锁情况下的资源竞争
  • 二、使用redis加锁

微服务的项目中,一个服务我们启动多份,在不同的进程中。这些服务是无状态的,而由数据存储容器(mysql/redis/es)进行状态数据的持久化。这就会导致资源竞争,出现多线程的问题。

一、模拟没有锁情况下的资源竞争

public class CommonConsumerService {
    //库存个数
    static int goodsCount = 900;
    //卖出个数
    static int saleCount = 0;
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                try {Thread.sleep(2);} catch (InterruptedException e) {}
                if (goodsCount > 0) {
                    goodsCount--;
                    System.out.println("剩余库存:" + goodsCount + " 卖出个数" + ++saleCount);
                }
            }).start();
        }
        Thread.sleep(3000);
    }
}

运行一次,最后几行的输出结果如下,很明显出错了,剩余0个商品却只卖出了899个商品,很明显有商品被某个线程私吞了。

...
剩余库存:5 卖出个数893
剩余库存:5 卖出个数894
剩余库存:4 卖出个数895
剩余库存:2 卖出个数896
剩余库存:2 卖出个数897
剩余库存:1 卖出个数898
剩余库存:0 卖出个数899

二、使用redis加锁

redis是单线程的,串行执行,那么接下来使用redis为资源进行加锁。

1.首先引入依赖

compile "org.springframework.boot:spring-boot-starter-data-redis"

2.引入redis加锁工具类

package com.kingboy.common.utils;
import redis.clients.jedis.Jedis;
import java.util.Collections;
/**
 * @author kingboy--KingBoyWorld@163.com
 * @date 2017/12/29 下午1:57
 * @desc Redis工具.
 */
public class RedisTool {
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long RELEASE_SUCCESS = 1L;
    /**
     * 尝试获取分布式锁
     * @param jedis      Redis客户端
     * @param lockKey    锁
     * @param requestId  请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
    /**
     * 释放分布式锁
     * @param jedis     Redis客户端
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
}

3.将上面没有锁的示例代码改编如下:

public class RedisLockConsumerService {
    //库存个数
    static int goodsCount = 900;
    //卖出个数
    static int saleCount = 0;
    @SneakyThrows
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "192.168.0.130", 6379, 1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                try {Thread.sleep(2);} catch (InterruptedException e) {}
                Jedis jedis = jedisPool.getResource();
                boolean lock = false;
                while (!lock) {
                    lock = RedisTool.tryGetDistributedLock(jedis, "goodsCount", Thread.currentThread().getName(), 10);
                }
                if (lock) {
                    if (goodsCount > 0) {
                        goodsCount--;
                        System.out.println("剩余库存:" + goodsCount + " 卖出个数" + ++saleCount);
                    }
                }
                RedisTool.releaseDistributedLock(jedis, "goodsCount", Thread.currentThread().getName());
                jedis.close();
            }).start();
        }
        Thread.sleep(3000);
        jedisPool.close();
    }
}

执行几次程序输出结果如下,可以看到结果是有序,并且正确的。

...
剩余库存:6 卖出个数894
剩余库存:5 卖出个数895
剩余库存:4 卖出个数896
剩余库存:3 卖出个数897
剩余库存:2 卖出个数898
剩余库存:1 卖出个数899
剩余库存:0 卖出个数900

以上就是SpringBoot中使用Redis作为全局锁示例过程的详细内容,更多关于SpringBoot Redis全局锁的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot整合Redis正确的实现分布式锁的示例代码

    前言 最近在做分块上传的业务,使用到了Redis来维护上传过程中的分块编号. 每上传完成一个分块就获取一下文件的分块集合,加入新上传的编号,手动接口测试下是没有问题的,前端通过并发上传调用就出现问题了,并发的get再set,就会存在覆盖写现象,导致最后的分块数据不对,不能触发分块合并请求. 遇到并发二话不说先上锁,针对执行代码块加了一个JVM锁之后问题就解决了. 仔细一想还是不太对,项目是分布式部署的,做了负载均衡,一个节点的代码被锁住了,请求轮询到其他节点还是可以进行覆盖写,并没有解决到问题啊

  • SpringBoot使用Redis实现分布式锁

    前言 在单机应用时代,我们对一个共享的对象进行多线程访问的时候,使用java的synchronized关键字或者ReentrantLock类对操作的对象加锁就可以解决对象的线程安全问题. 分布式应用时代这个方法却行不通了,我们的应用可能被部署到多台机器上,运行在不同的JVM里,一个对象可能同时存在多台机器的内存中,怎样使共享对象同时只被一个线程处理就成了一个问题. 在分布式系统中为了保证一个对象在高并发的情况下只能被一个线程使用,我们需要一种跨JVM的互斥机制来控制共享资源的访问,此时就需要用到

  • 基于springboot实现redis分布式锁的方法

    在公司的项目中用到了分布式锁,但只会用却不明白其中的规则 所以写一篇文章来记录 使用场景:交易服务,使用redis分布式锁,防止重复提交订单,出现超卖问题 分布式锁的实现方式 基于数据库乐观锁/悲观锁 Redis分布式锁(本文) Zookeeper分布式锁 redis是如何实现加锁的? 在redis中,有一条命令,实现锁 SETNX key value 该命令的作用是将 key 的值设为 value ,当且仅当 key 不存在.若给定的 key 已经存在,则 SETNX不做任何动作.设置成功,返

  • springboot redis分布式锁代码实例

    这篇文章主要介绍了springboot redis分布式锁代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 随着微服务等分布式架构的快速发展及应用,在很多情况下,我们都会遇到在并发情况下多个线程竞争资源的情况,比如我们耳熟能详的秒杀活动,多平台多用户对同一个资源进行操作等场景等.分布式锁的实现方式有很多种,比如基于数据库.Zookeeper.Redis等,本文我们主要介绍Spring Boot整合Redis实现分布式锁. 工具类如下: i

  • SpringBoot集成Redisson实现分布式锁的方法示例

    上篇 <SpringBoot 集成 redis 分布式锁优化>对死锁的问题进行了优化,今天介绍的是 redis 官方推荐使用的 Redisson ,Redisson 架设在 redis 基础上的 Java 驻内存数据网格(In-Memory Data Grid),基于NIO的 Netty 框架上,利用了 redis 键值数据库.功能非常强大,解决了很多分布式架构中的问题. Github的wiki地址: https://github.com/redisson/redisson/wiki 官方文档

  • SpringBoot集成redis实现分布式锁的示例代码

    1.准备 使用redis实现分布式锁,需要用的setnx(),所以需要集成Jedis 需要引入jar,jar最好和redis的jar版本对应上,不然会出现版本冲突,使用的时候会报异常redis.clients.jedis.Jedis.set(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String; 我使用的redis版本是2.3.0,Jedis使用的是3.3.0 <de

  • SpringBoot中使用Redis作为全局锁示例过程

    目录 一.模拟没有锁情况下的资源竞争 二.使用redis加锁 微服务的项目中,一个服务我们启动多份,在不同的进程中.这些服务是无状态的,而由数据存储容器(mysql/redis/es)进行状态数据的持久化.这就会导致资源竞争,出现多线程的问题. 一.模拟没有锁情况下的资源竞争 public class CommonConsumerService { //库存个数 static int goodsCount = 900; //卖出个数 static int saleCount = 0; publi

  • SpringBoot中使用redis做分布式锁的方法

    一.模拟问题 最近在公司遇到一个问题,挂号系统是做的集群,比如启动了两个相同的服务,病人挂号的时候可能会出现同号的情况,比如两个病人挂出来的号都是上午2号.这就出现了问题,由于是集群部署的,所以单纯在代码中的方法中加锁是不能解决这种情况的.下面我将模拟这种情况,用redis做分布式锁来解决这个问题. 1.新建挂号明细表 2.在idea上新建项目 下图是创建好的项目结构,上面那个parent项目是其他项目不用管它,和新建的没有关系 3.开始创建controller,service,dao(mapp

  • SpringBoot中使用Redis Stream实现消息监听示例

    目录 Demo环境 仓库地址 POM依赖 配置监听消息类 监听俩个stream的实现 [问题补充]确认完消息删除消息 [问题补充]自动初始化stream的key和group问题-最新更新-2021年12月4日 Demo环境 JDK8 Maven3.6.3 springboot2.4.3 redis_version:6.2.1 仓库地址 https://gitee.com/hlovez/redismq.git. POM依赖 <?xml version="1.0" encoding=

  • springboot中使用redis的方法代码详解

    特别说明: 本文针对的是新版 spring boot 2.1.3,其 spring data 依赖为 spring-boot-starter-data-redis,且其默认连接池为 lettuce ​redis 作为一个高性能的内存数据库,如果不会用就太落伍了,之前在 node.js 中用过 redis,本篇记录如何将 redis 集成到 spring boot 中.提供 redis 操作类,和注解使用 redis 两种方式.主要内容如下: •docker 安装 redis •springboo

  • 在SpringBoot中添加Redis及配置方法

    在实际的开发中,会有这样的场景.有一个微服务需要提供一个查询的服务,但是需要查询的数据库表的数据量十分庞大,查询所需要的时间很长. 此时就可以考虑在项目中加入缓存. 引入依赖 在maven项目中引入如下依赖.并且需要在本地安装redis. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifac

  • SpringBoot中默认缓存实现方案的示例代码

    在上一节中,我带大家学习了在Spring Boot中对缓存的实现方案,尤其是结合Spring Cache的注解的实现方案,接下来在本章节中,我带大家通过代码来实现. 一. Spring Boot实现默认缓存 1. 创建web项目 我们按照之前的经验,创建一个web程序,并将之改造成Spring Boot项目,具体过程略. 2. 添加依赖包 <dependency> <groupId>org.springframework.boot</groupId> <artif

  • SpringBoot之使用Redis实现分布式锁(秒杀系统)

    一.Redis分布式锁概念篇 建议直接采用Redis的官方推荐的Redisson作为redis的分布式锁 1.1.为什么要使用分布式锁 我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug! 注意这是单机应用,也就是所有的请求都会分配到当前服务器的JVM内部,然后映射为操作系统的线程进行处理!而这个共享变量只是在这个JVM内部的一块内存空间! 后来业务发展,需要做集群,一个应用需要部署到几台机

  • springboot中使用redis由浅入深解析

    正文 很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下 这里假设已经成功创建了一个springboot项目. redis连接工厂类 第一步,需要加上springboot的redis jar包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redi

  • SpringBoot中使用Redis的完整实例

    一.在SpringBoot中使用Redis的一套军体拳 1.导包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.2.0.RELEASE</version> </dependency> 2.导入工具类 packag

  • java、spring、springboot中整合Redis的详细讲解

    java整合Redis 1.引入依赖或者导入jar包 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> 2.代码实现 public class JedisTest { public static void main(String[]

随机推荐