Redis的分布式锁实现代码(商品秒杀)

Redis的分布式锁实现代码(商品秒杀)

准备工作

下载Apache JMeter软件:模拟多线程,高并发情况

导入依赖

导入redis包
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

分布式锁代码

该方法有三个接口
①init():初始化商品数量为100
②synchronizedBuy():使用synchronizedBuy锁模拟商品秒杀
③redisBuy():使用redis分布式锁模拟商品秒杀
@RestController
public class OrderController {

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/initCount")
    public String init(){
        redisTemplate.opsForValue().set("count","100");
        return "库存初始化成功";
    }


    /**
     * @author:
     * @description: synchronized锁
     * @date: 2021/10/23
     * @param
     * @return java.lang.String
     */
    @GetMapping("/synchronizedBuy")
    public String synchronizedBuy(){
        synchronized (OrderController.class){
            Integer count = Integer.parseInt(redisTemplate.opsForValue().get("count").toString());
            if (count > 0)
            {
                Integer realCount = count - 1;
                System.out.println("购买商品成功,库存为:" + realCount.toString());
                redisTemplate.opsForValue().set("count",realCount.toString());
            }else {
                System.out.println("库存不足,购买失败");
            }
            return "ok";
        }
    }


    /**
     * @author:
     * @description: 使用分布式锁
     * @date: 2021/10/23
     * @param
     * @return java.lang.String
     */
    @GetMapping("/redisBuy")
    public String redisBuy(){
        String lockKey = "ShangPin";
        String clientId = UUID.randomUUID().toString();
        try {
            //setnx加锁
            //setIfAbsent()方法:如果这个锁已经存在,则返回flase,获得锁失败,如果返回true则说明加锁成功
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10, TimeUnit.SECONDS);
            if (lock)
            {
                //执行购买业务
                int count1 = Integer.parseInt(redisTemplate.opsForValue().get("count").toString());
                if (count1 > 0)
                {
                    Integer realCount = count1 - 1;
                    System.out.println("购买商品成功,库存为:" + realCount.toString());
                    redisTemplate.opsForValue().set("count",realCount.toString());
                }else {
                    System.out.println("库存不足,购买失败");
                }
            }else {
                try {
                     //获取锁失败,每隔0.1秒再获取
                     Thread.sleep(100);
                     redisBuy();
                }catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //删锁,判断当前客户端的clientId与redis持有的一样
            if(clientId.equals(redisTemplate.opsForValue().get(lockKey)))
            redisTemplate.delete(lockKey);
        }
        return "ok";
    }
}

启动项目

为了模拟集群环境,idea需要启动两个端口不一样的服务,方法如下:
第一步
第二步
修改好后,端口为17055时启动项目
在这里插入图片描述
再把端口修改为17054,再次启动项目
在这里插入图片描述

启动Jmeter

使用Apache Jmeter软件模拟多线程并发。

Jmeter分别调用17055端口和17054端口的synchronizedBuy()接口和redisBuy()接口,分别模拟80个线程,以及17055端口的init()接口,具体配置如下。

在这里插入图片描述
在这里插入图片描述

调用17054的synchronizedBuy()接口
在这里插入图片描述
在这里插入图片描述
调用17055的synchronizedBuy()接口
在这里插入图片描述
其余几个接口可仿照上面内容进行创建。

运行结果

首先调用init()接口进行商品初始化操作。

单击环境下,使用synchronized锁

只调用一个接口,结果如下所示:
在这里插入图片描述
商品有100件,只模拟了80个线程,所有商品应该卖出80件,可以看出在单击环境下是哟synchronized锁没有问题。

购买商品成功,库存为:99
购买商品成功,库存为:98
购买商品成功,库存为:97
购买商品成功,库存为:96
购买商品成功,库存为:95
购买商品成功,库存为:94
购买商品成功,库存为:93
购买商品成功,库存为:92
购买商品成功,库存为:91
购买商品成功,库存为:90
购买商品成功,库存为:89
购买商品成功,库存为:88
购买商品成功,库存为:87
购买商品成功,库存为:86
购买商品成功,库存为:85
购买商品成功,库存为:84
购买商品成功,库存为:83
购买商品成功,库存为:82
购买商品成功,库存为:81
购买商品成功,库存为:80
购买商品成功,库存为:79
购买商品成功,库存为:78
购买商品成功,库存为:77
购买商品成功,库存为:76
购买商品成功,库存为:75
购买商品成功,库存为:74
购买商品成功,库存为:73
购买商品成功,库存为:72
购买商品成功,库存为:71
购买商品成功,库存为:70
购买商品成功,库存为:69
购买商品成功,库存为:68
购买商品成功,库存为:67
购买商品成功,库存为:66
购买商品成功,库存为:65
购买商品成功,库存为:64
购买商品成功,库存为:63
购买商品成功,库存为:62
购买商品成功,库存为:61
购买商品成功,库存为:60
购买商品成功,库存为:59
购买商品成功,库存为:58
购买商品成功,库存为:57
购买商品成功,库存为:56
购买商品成功,库存为:55
购买商品成功,库存为:54
购买商品成功,库存为:53
购买商品成功,库存为:52
购买商品成功,库存为:51
购买商品成功,库存为:50
购买商品成功,库存为:49
购买商品成功,库存为:48
购买商品成功,库存为:47
购买商品成功,库存为:46
购买商品成功,库存为:45
购买商品成功,库存为:44
购买商品成功,库存为:43
购买商品成功,库存为:42
购买商品成功,库存为:41
购买商品成功,库存为:40
购买商品成功,库存为:39
购买商品成功,库存为:38
购买商品成功,库存为:37
购买商品成功,库存为:36
购买商品成功,库存为:35
购买商品成功,库存为:34
购买商品成功,库存为:33
购买商品成功,库存为:32
购买商品成功,库存为:31
购买商品成功,库存为:30
购买商品成功,库存为:29
购买商品成功,库存为:28
购买商品成功,库存为:27
购买商品成功,库存为:26
购买商品成功,库存为:25
购买商品成功,库存为:24
购买商品成功,库存为:23
购买商品成功,库存为:22
购买商品成功,库存为:21
购买商品成功,库存为:20

集群环境下,使用synchronized锁

同时选中调用两个不同端口的synchronizedBuy()接口( 模拟集群环境下使用synchronized锁实现商品秒杀的现象)
在这里插入图片描述
结果如下:在这里插入图片描述
在这里插入图片描述

购买商品成功,库存为:95
购买商品成功,库存为:94
购买商品成功,库存为:93
购买商品成功,库存为:92
购买商品成功,库存为:91
购买商品成功,库存为:90
购买商品成功,库存为:89
购买商品成功,库存为:88
购买商品成功,库存为:87
购买商品成功,库存为:86
购买商品成功,库存为:85
购买商品成功,库存为:84
购买商品成功,库存为:83
购买商品成功,库存为:82
购买商品成功,库存为:81
购买商品成功,库存为:80
购买商品成功,库存为:78
购买商品成功,库存为:77
购买商品成功,库存为:76
购买商品成功,库存为:75
购买商品成功,库存为:74
购买商品成功,库存为:73
购买商品成功,库存为:72
购买商品成功,库存为:71
购买商品成功,库存为:70
购买商品成功,库存为:69
购买商品成功,库存为:67
购买商品成功,库存为:65
购买商品成功,库存为:64
购买商品成功,库存为:62
购买商品成功,库存为:61
购买商品成功,库存为:60
购买商品成功,库存为:58
购买商品成功,库存为:57
购买商品成功,库存为:56
购买商品成功,库存为:55
购买商品成功,库存为:54
购买商品成功,库存为:53
购买商品成功,库存为:51
购买商品成功,库存为:50
购买商品成功,库存为:49
购买商品成功,库存为:48
购买商品成功,库存为:47
购买商品成功,库存为:46
购买商品成功,库存为:45
购买商品成功,库存为:43
购买商品成功,库存为:42
购买商品成功,库存为:40
购买商品成功,库存为:39
购买商品成功,库存为:38
购买商品成功,库存为:37
购买商品成功,库存为:36
购买商品成功,库存为:35
购买商品成功,库存为:34
购买商品成功,库存为:33
购买商品成功,库存为:32
购买商品成功,库存为:30
购买商品成功,库存为:28
购买商品成功,库存为:27
购买商品成功,库存为:25
购买商品成功,库存为:24
购买商品成功,库存为:23
购买商品成功,库存为:22
购买商品成功,库存为:21
购买商品成功,库存为:19
购买商品成功,库存为:18
购买商品成功,库存为:17
购买商品成功,库存为:15
购买商品成功,库存为:13
购买商品成功,库存为:12
购买商品成功,库存为:11
购买商品成功,库存为:10
购买商品成功,库存为:9
购买商品成功,库存为:8
购买商品成功,库存为:7
购买商品成功,库存为:6
购买商品成功,库存为:5
购买商品成功,库存为:4
购买商品成功,库存为:3
购买商品成功,库存为:2
购买商品成功,库存为:99
购买商品成功,库存为:98
购买商品成功,库存为:97
购买商品成功,库存为:96
购买商品成功,库存为:95
购买商品成功,库存为:94
购买商品成功,库存为:93
购买商品成功,库存为:92
购买商品成功,库存为:92
购买商品成功,库存为:91
购买商品成功,库存为:90
购买商品成功,库存为:89
购买商品成功,库存为:88
购买商品成功,库存为:87
购买商品成功,库存为:86
购买商品成功,库存为:85
购买商品成功,库存为:84
购买商品成功,库存为:83
购买商品成功,库存为:82
购买商品成功,库存为:80
购买商品成功,库存为:79
购买商品成功,库存为:78
购买商品成功,库存为:77
购买商品成功,库存为:76
购买商品成功,库存为:71
购买商品成功,库存为:70
购买商品成功,库存为:69
购买商品成功,库存为:68
购买商品成功,库存为:66
购买商品成功,库存为:65
购买商品成功,库存为:64
购买商品成功,库存为:63
购买商品成功,库存为:62
购买商品成功,库存为:61
购买商品成功,库存为:60
购买商品成功,库存为:59
购买商品成功,库存为:57
购买商品成功,库存为:56
购买商品成功,库存为:55
购买商品成功,库存为:54
购买商品成功,库存为:53
购买商品成功,库存为:52
购买商品成功,库存为:51
购买商品成功,库存为:50
购买商品成功,库存为:49
购买商品成功,库存为:48
购买商品成功,库存为:47
购买商品成功,库存为:46
购买商品成功,库存为:45
购买商品成功,库存为:44
购买商品成功,库存为:43
购买商品成功,库存为:42
购买商品成功,库存为:41
购买商品成功,库存为:40
购买商品成功,库存为:39
购买商品成功,库存为:38
购买商品成功,库存为:37
购买商品成功,库存为:36
购买商品成功,库存为:35
购买商品成功,库存为:34
购买商品成功,库存为:32
购买商品成功,库存为:31
购买商品成功,库存为:30
购买商品成功,库存为:29
购买商品成功,库存为:28
购买商品成功,库存为:27
购买商品成功,库存为:26
购买商品成功,库存为:25
购买商品成功,库存为:24
购买商品成功,库存为:23
购买商品成功,库存为:22
购买商品成功,库存为:21
购买商品成功,库存为:20
购买商品成功,库存为:19
购买商品成功,库存为:17
购买商品成功,库存为:16
购买商品成功,库存为:15
购买商品成功,库存为:14
购买商品成功,库存为:13
购买商品成功,库存为:12

可以看出商品已经超卖了。

集群环境下,使用redis分布式锁

在这里插入图片描述
运行结果:

在这里插入图片描述

购买商品成功,库存为:94
购买商品成功,库存为:93
购买商品成功,库存为:88
购买商品成功,库存为:87
购买商品成功,库存为:85
购买商品成功,库存为:83
购买商品成功,库存为:82
购买商品成功,库存为:81
购买商品成功,库存为:79
购买商品成功,库存为:73
购买商品成功,库存为:67
购买商品成功,库存为:62
购买商品成功,库存为:61
购买商品成功,库存为:58
购买商品成功,库存为:57
购买商品成功,库存为:56
购买商品成功,库存为:55
购买商品成功,库存为:50
购买商品成功,库存为:49
购买商品成功,库存为:47
购买商品成功,库存为:46
购买商品成功,库存为:45
购买商品成功,库存为:44
购买商品成功,库存为:43
购买商品成功,库存为:41
购买商品成功,库存为:40
购买商品成功,库存为:39
购买商品成功,库存为:38
购买商品成功,库存为:37
购买商品成功,库存为:34
购买商品成功,库存为:33
购买商品成功,库存为:32
购买商品成功,库存为:28
购买商品成功,库存为:27
购买商品成功,库存为:23
购买商品成功,库存为:22
购买商品成功,库存为:18
购买商品成功,库存为:17
购买商品成功,库存为:15
购买商品成功,库存为:14
购买商品成功,库存为:13
购买商品成功,库存为:12
购买商品成功,库存为:9
购买商品成功,库存为:8
购买商品成功,库存为:7
购买商品成功,库存为:4
购买商品成功,库存为:3
购买商品成功,库存为:2
库存不足,购买失败
库存不足,购买失败
库存不足,购买失败
购买商品成功,库存为:86
购买商品成功,库存为:84
购买商品成功,库存为:80
购买商品成功,库存为:78
购买商品成功,库存为:77
购买商品成功,库存为:76
购买商品成功,库存为:75
购买商品成功,库存为:74
购买商品成功,库存为:72
购买商品成功,库存为:71
购买商品成功,库存为:70
购买商品成功,库存为:69
购买商品成功,库存为:68
购买商品成功,库存为:66
购买商品成功,库存为:65
购买商品成功,库存为:64
购买商品成功,库存为:63
购买商品成功,库存为:60
购买商品成功,库存为:59
购买商品成功,库存为:54
购买商品成功,库存为:53
购买商品成功,库存为:52
购买商品成功,库存为:51
购买商品成功,库存为:48
购买商品成功,库存为:42
购买商品成功,库存为:36
购买商品成功,库存为:35
购买商品成功,库存为:31
购买商品成功,库存为:30
购买商品成功,库存为:29
购买商品成功,库存为:26
购买商品成功,库存为:25
购买商品成功,库存为:24
购买商品成功,库存为:21
购买商品成功,库存为:20
购买商品成功,库存为:19
购买商品成功,库存为:16
购买商品成功,库存为:11
购买商品成功,库存为:10
购买商品成功,库存为:6
购买商品成功,库存为:5
购买商品成功,库存为:1
购买商品成功,库存为:0
库存不足,购买失败
库存不足,购买失败

可以看出集群环境下使用redis分布式锁刚好买了100件商品,不会出现超卖现象。

下面是一个简单的Java代码,结合Redis分布式锁实现秒杀功能: ```java public class RedisLock { private static final String LOCK_PREFIX = "redis_lock_"; private JedisPool jedisPool; public RedisLock(JedisPool jedisPool) { this.jedisPool = jedisPool; } /** * 获取 * @param lockName 名称 * @param timeout 超时时间,单位为秒 * @return 是否获取到 */ public boolean lock(String lockName, int timeout) { Jedis jedis = null; try { jedis = jedisPool.getResource(); String lockKey = LOCK_PREFIX + lockName; long start = System.currentTimeMillis(); while (true) { String result = jedis.set(lockKey, "locked", "NX", "EX", timeout); if ("OK".equals(result)) { return true; } long end = System.currentTimeMillis(); if ((end - start) > (timeout * 1000 - 100)) { return false; } Thread.sleep(100); } } catch (Exception e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } return false; } /** * 释放 * @param lockName 名称 * @return 是否释放成功 */ public boolean release(String lockName) { Jedis jedis = null; try { jedis = jedisPool.getResource(); String lockKey = LOCK_PREFIX + lockName; jedis.del(lockKey); return true; } catch (Exception e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } return false; } } ``` 接下来是使用Redis实现秒杀功能的代码示例: ```java public class SeckillService { private RedisLock redisLock; public SeckillService(RedisLock redisLock) { this.redisLock = redisLock; } /** * 秒杀商品 * @param productId 商品ID * @param userId 用户ID * @return 是否秒杀成功 */ public boolean seckill(String productId, String userId) { boolean lockResult = redisLock.lock(productId, 10); // 获取 if (!lockResult) { return false; // 没有获取到,表示秒杀失败 } try { // TODO: 执行秒杀操作 return true; // 秒杀成功 } finally { redisLock.release(productId); // 释放 } } } ``` 在使用Redis分布式锁时,需要特别注意以下几点: 1. 的名称要尽可能唯一,以免与其他发生冲突。 2. 的超时时间应该设置得足够短,避免因为一直被占用而导致其他请求无法执行。 3. 在获取时,需要使用set命令的NX和EX选项,避免因为竞争而导致的死问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值