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 相关文章推荐
Java Optional<Foo>转换成List<Bar>的实例方法
Jun 20 Java/Android
详解SpringBoot异常处理流程及原理
Jun 21 Java/Android
Java 中的 Unsafe 魔法类的作用大全
Jun 26 Java/Android
详解Spring事件发布与监听机制
Jun 30 Java/Android
Java图书管理系统,课程设计必用(源码+文档)
Jun 30 Java/Android
SSM项目使用拦截器实现登录验证功能
Jan 22 Java/Android
正则表达式拆分url实例代码
Feb 24 Java/Android
关于ObjectUtils.isEmpty() 和 null 的区别
Feb 28 Java/Android
springboot 自定义配置 解决Boolean属性不生效
Mar 18 Java/Android
详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类
Apr 08 Java/Android
Spring Cloud OpenFeign模版化客户端
Jun 25 Java/Android
Java实现贪吃蛇游戏的示例代码
Sep 23 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
Access数据库导入Mysql的方法之一
2006/10/09 PHP
基于mysql的论坛(6)
2006/10/09 PHP
什么是MVC,好东西啊
2007/05/03 PHP
Session服务器配置指南与使用经验的深入解析
2013/06/17 PHP
php对数组排序的简单实例
2013/12/25 PHP
使用PHP和JavaScript判断请求是否来自微信内浏览器
2015/08/18 PHP
php中pcntl_fork创建子进程的方法实例
2019/03/14 PHP
从父页面读取和操作iframe中内容方法
2009/07/25 Javascript
js+jquery实现图片裁剪功能
2015/01/02 Javascript
jQuery实现折线图的方法
2015/02/28 Javascript
js检测判断日期大于多少天的方法
2015/05/04 Javascript
JS正则匹配中文的方法示例
2017/01/06 Javascript
基于JavaScript实现拖动滑块效果
2017/02/16 Javascript
详解如何修改 node_modules 里的文件
2020/05/22 Javascript
[44:10]2018DOTA2亚洲邀请赛 4.5 淘汰赛 EG vs VP 第一场
2018/04/06 DOTA
Python实现的下载8000首儿歌的代码分享
2014/11/21 Python
实践Python的爬虫框架Scrapy来抓取豆瓣电影TOP250
2016/01/20 Python
Python探索之创建二叉树
2017/10/25 Python
解决Python中pandas读取*.csv文件出现编码问题
2019/07/12 Python
Pytorch卷积层手动初始化权值的实例
2019/08/17 Python
django自带调试服务器的使用详解
2019/08/29 Python
python数据化运营的重要意义
2019/11/25 Python
pytorch torch.nn.AdaptiveAvgPool2d()自适应平均池化函数详解
2020/01/03 Python
解决Python 写文件报错TypeError的问题
2020/10/23 Python
毕业生求职简历中的自我评价
2013/10/18 职场文书
夏季奶茶店创业计划书
2014/01/16 职场文书
课程改革实施方案
2014/03/16 职场文书
给市场的环保建议书
2014/05/14 职场文书
2014年六一儿童节演讲稿
2014/05/23 职场文书
汽车维修求职信
2014/06/15 职场文书
岗位职责说明书模板
2014/07/30 职场文书
2014年质量管理工作总结
2014/12/01 职场文书
焦裕禄纪念馆观后感
2015/06/09 职场文书
女性健康讲座主持词
2015/07/04 职场文书
MySQL的意向共享锁、意向排它锁和死锁
2022/07/15 MySQL
SQLServer常见数学函数梳理总结
2022/08/05 MySQL