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


Posted in Java/Android onMarch 24, 2022

微服务的项目中,一个服务我们启动多份,在不同的进程中。这些服务是无状态的,而由数据存储容器(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全局锁的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
eclipse创建项目没有dynamic web的解决方法
Jun 24 Java/Android
Java常用工具类汇总 附示例代码
Jun 26 Java/Android
Netty结合Protobuf进行编解码的方法
Jun 26 Java/Android
深入理解java.lang.String类的不可变性
Jun 27 Java/Android
Java 数组内置函数toArray详解
Jun 28 Java/Android
实体类或对象序列化时,忽略为空属性的操作
Jun 30 Java/Android
SpringBoot 拦截器妙用你真的了解吗
Jul 01 Java/Android
Sleuth+logback 设置traceid 及自定义信息方式
Jul 26 Java/Android
Java 实现限流器处理Rest接口请求详解流程
Nov 02 Java/Android
SpringDataJPA实体类关系映射配置方式
Dec 06 Java/Android
IDEA 2022 Translation 未知错误 翻译文档失败
Apr 24 Java/Android
spring boot实现文件上传
Aug 14 Java/Android
java项目构建Gradle的使用教程
Mar 24 #Java/Android
SpringBoot2零基础到精通之数据与页面响应
MybatisPlus EntityWrapper如何自定义SQL
Mar 22 #Java/Android
SpringBoot2零基础到精通之数据库专项精讲
Mar 22 #Java/Android
Spring Bean是如何初始化的详解
Mar 22 #Java/Android
关于EntityWrapper的in用法
Mar 22 #Java/Android
SpringBoot2零基础到精通之异常处理与web原生组件注入
Mar 22 #Java/Android
You might like
德生1994机评
2021/03/02 无线电
19个Android常用工具类汇总
2014/12/30 PHP
Linux系统下php获得系统分区信息的方法
2015/03/30 PHP
PDO::rollBack讲解
2019/01/29 PHP
php服务器的系统详解
2019/10/12 PHP
Javascript 继承机制实例
2009/08/12 Javascript
jQuery Ajax 实例全解析
2011/04/20 Javascript
javascript时间自动刷新实现原理与步骤
2013/01/06 Javascript
一个可以增加和删除行的table并可编辑表格中内容
2014/06/16 Javascript
使用jQuery+EasyUI实现CheckBoxTree的级联选中特效
2015/12/06 Javascript
详解JavaScript权威指南之对象
2016/09/27 Javascript
JavaScript数组复制详解
2017/02/02 Javascript
JavaScript中this的用法及this在不同应用场景的作用解析
2017/04/13 Javascript
JS跳转手机站url的若干注意事项
2017/10/18 Javascript
JavaScript实现异步图像上传功能
2018/07/12 Javascript
了解javascript中的Dom操作
2019/05/27 Javascript
Vue使用NProgress进度条的方法
2019/09/21 Javascript
微信小程序登陆注册功能的实现代码
2019/12/10 Javascript
[55:02]2014 DOTA2国际邀请赛中国区预选赛 HGT VS Orenda
2014/05/21 DOTA
Python中实现远程调用(RPC、RMI)简单例子
2014/04/28 Python
用Python shell简化开发
2018/08/08 Python
基于Python3.6+splinter实现自动抢火车票
2018/09/25 Python
python清除字符串前后空格函数的方法
2018/10/21 Python
Flask之请求钩子的实现
2018/12/23 Python
python输出数组中指定元素的所有索引示例
2019/12/06 Python
TensorFlow 输出checkpoint 中的变量名与变量值方式
2020/02/11 Python
浅谈JupyterNotebook导出pdf解决中文的问题
2020/04/22 Python
美国汽配连锁巨头Pep Boys官网:轮胎更换、汽车维修服务和汽车零部件
2017/01/14 全球购物
如果一个类实现了多个接口但是这些接口有相同的方法名将会怎样
2013/06/16 面试题
青年教师师德演讲稿
2014/08/26 职场文书
2015年煤矿安全工作总结
2015/05/23 职场文书
人与自然观后感
2015/06/16 职场文书
某学校的2019年度工作报告范本
2019/10/11 职场文书
Python实现抖音热搜定时爬取功能
2022/03/16 Python
阿里云日志过滤器配置日志服务
2022/04/09 Servers
python语言中pandas字符串分割str.split()函数
2022/08/05 Python