SpringBoot中使用Redisson分布式锁的应用场景-多线程、服务、节点秒杀/抢票处理

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 SpringBoot中使用Redisson分布式锁的应用场景-多线程、服务、节点秒杀/抢票处理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

场景

若依前后端分离版手把手教你本地搭建环境并运行项目:

若依前后端分离版手把手教你本地搭建环境并运行项目_霸道流氓气质的博客-CSDN博客_前后端分离项目本地运行

在上面搭建起来前后端分离的项目,如果在某些业务场景下比如抢票、秒杀时会有多线程、多定位任务、多服务节点

对同一个redis中的key进行获取、更改和存储的操作。

如果每次进行操作时不进行加锁处理,就会导致数据不准确(多卖、少卖)的情况。

注:

博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。

实现

1、Redisson

Redisson – Redis Java client
with features of an in-memory data grid。

Redisson – Redis Java 客户端

具有内存数据网格的特征。

官方文档地址:

目录 · redisson/redisson Wiki · GitHub

gitHub地址:

GitHub – redisson/redisson: Redisson – Redis Java client with features of In-Memory Data Grid. Over 50 Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Publish / Subscribe, Bloom filter, Spring Cache, Tomcat, Scheduler, JCache API, Hibernate, MyBatis, RPC, local cache …

2、参考官方文档快速开始

引入maven依赖

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

新建Redisson配置类,配置redis的连接地址等信息

package com.ruoyi.quartz.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfiguration {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    @Value("${spring.redis.password}")
    private String password;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        String url = "redis://" + host + ":" + port;
        config.useSingleServer().setAddress(url).setPassword(password).setDatabase(0);
        return Redisson.create(config);
    }

}

这里的redis的相关信息已经在配置文件中进行配置

SpringBoot中使用Redisson分布式锁的应用场景-多线程、服务、节点秒杀/抢票处理

 

3、模拟一个多线程的任务,同时对redis中存储的num这个key进行操作,这个key代表票或者商品的总数量。

如果不加锁,任由所有任务都去获取同一个key,进行减1操作并存储回去,代码实现

        ExecutorService executorService = Executors.newFixedThreadPool(50);
        //存储每个线程的返回结果
        ArrayList<Future<Integer>> futureArrayList = new ArrayList<>();
        //模拟50个任务发起购票/秒杀商品操作,每个任务买一个
        for (int i = 0; i < 50; i++) {
            Future<Integer> submit = executorService.submit(() -> {
                int count = 0;
                int num = redisCache.getCacheObject("num");
                num--;
                redisCache.setCacheObject("num", num);
                count++;
                return count;
            });
            futureArrayList.add(submit);
        }

        Integer saleCount = 0;

        for (int i = 0; i < futureArrayList.size(); i++) {
            Future<Integer> integerFuture = futureArrayList.get(i);
            saleCount = saleCount + integerFuture.get();
        }
        System.out.println("累计卖出:"+saleCount);

关于多线程的使用方式可以参考下文

Java中ExecutorService线程池的使用(Runnable和Callable多线程实现):

Java中ExecutorService线程池的使用(Runnable和Callable多线程实现)_霸道流氓气质的博客-CSDN博客

这种不加任何锁的实现方式,导致的结果是

累计计数卖出的是50,实际上redis中减少的数量却不是

SpringBoot中使用Redisson分布式锁的应用场景-多线程、服务、节点秒杀/抢票处理

 SpringBoot中使用Redisson分布式锁的应用场景-多线程、服务、节点秒杀/抢票处理

 

4、所有为了保持一致性,需要给每次操作同一个key之前添加锁

获取一把锁,只要锁的名字一样,就是一把锁

RLock numLock = redissonClient.getLock("numLock");

每个线程操作redis之前加锁

                if(numLock.tryLock(1,1,TimeUnit.SECONDS)){
                    int num = redisCache.getCacheObject("num");
                    num--;
                    redisCache.setCacheObject("num", num);
                    count++;
                    //isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定
                    if(numLock.isHeldByCurrentThread()){
                        numLock.unlock();
                    }
                }

这里的tryLock的参数为最大等待时间为1秒,上锁1秒后自动解锁。

然后isHeldByCurrentThread的作用是查询当前线程是否保持此锁定。

当对redis操作结束之后,如果还保持此锁定,则调用unlock进行解锁。

完整示例代码

package com.ruoyi.quartz.task;

import com.ruoyi.common.core.redis.RedisCache;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.concurrent.*;

@Component("redissonDemoTask")
public class RedissonDemoTask {



    @Autowired
    private RedisCache redisCache;

    @Autowired
    RedissonClient redissonClient;

    public void PlatformOne() throws ExecutionException, InterruptedException {

        //加锁的实现方式
        ExecutorService executorService = Executors.newFixedThreadPool(50);
        ArrayList<Future<Integer>> futureArrayList = new ArrayList<>();
        RLock numLock = redissonClient.getLock("numLock");
        for (int i = 0; i < 50; i++) {
            Future<Integer> submit = executorService.submit(() -> {
                int count = 0;
                if(numLock.tryLock(1,1,TimeUnit.SECONDS)){
                    int num = redisCache.getCacheObject("num");
                    num--;
                    redisCache.setCacheObject("num", num);
                    count++;
                    //isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定
                    if(numLock.isHeldByCurrentThread()){
                        numLock.unlock();
                    }
                }
                return count;
            });
            futureArrayList.add(submit);
        }

        Integer saleCount = 0;

        for (int i = 0; i < futureArrayList.size(); i++) {
            Future<Integer> integerFuture = futureArrayList.get(i);
            saleCount = saleCount + integerFuture.get();
        }
        System.out.println("累计卖出:"+saleCount);
    }
}

加锁效果

SpringBoot中使用Redisson分布式锁的应用场景-多线程、服务、节点秒杀/抢票处理

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/135916.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!