redis 分布式锁

 第一步 redis分布式锁工具类

package com.example.demoone.redis;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;

/**
 * @author GXY
 * @Package com.an.redis.util
 * @date 2020/5/25 11:02
 * @Copyright © 2020-2021 新流通
 */
@Component
public class RedisTemplateHandler {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static  boolean COMMON_ERROR_KEY = Boolean.FALSE;

    /**
     * 插入分布式Job Redis锁
     * @param key 锁的key
     * @param val 锁的value
     * @param exprire 过期时间(秒)
     * @return
     */
    public boolean redisSetNX(String key,String val,long exprire){
        Boolean result  = stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection.setNX(key.getBytes(), val.getBytes());
        });
        // TODO-GXY: 2020/5/25 17:10 上锁与设置过期时间为两步,非原子操作,这里不够严谨。借助下面的redisCheckNX弥补;后期也可以优化成一步操作
        if (result){
            stringRedisTemplate.expire(key,exprire, TimeUnit.SECONDS);
        }

        return result ;
    }

    /**
     * 删除分布式Job Redis锁
     * @param key
     * @return
     */
    public boolean redisDelNX(String key){
        Boolean delete = stringRedisTemplate.delete(key);
        return delete;
    }

    /**
     *  检查分布式Job Redis锁
     * @param key
     * @param lockSeconds 给锁上的过期时长(秒)
     * @return
     */
    public boolean redisCheckNX(String key,int lockSeconds){
        //根据key获取过期时间
        Long expire = stringRedisTemplate.getExpire(key);
        String s = stringRedisTemplate.opsForValue().get(key);
        long time = 0;
        if (StringUtils.isNotBlank(s)){
            time = Calendar.getInstance().getTimeInMillis() - Long.valueOf(s).longValue();
        }else {
            //key不存在说明锁过期
            COMMON_ERROR_KEY = false;
            return COMMON_ERROR_KEY;
        }
        //超时过期
        if (expire <= 0 || time > lockSeconds * 1000L){
            //删除key,释放锁
            System.out.println("出现死锁,手动释放锁");
            redisDelNX(key);
            COMMON_ERROR_KEY = false;
            return COMMON_ERROR_KEY;
        }
        COMMON_ERROR_KEY = true;
        return COMMON_ERROR_KEY;
    }
}

第二步 简单测试类

package com.example.demoone.abc;

import com.example.demoone.redis.RedisLock;
import com.example.demoone.redis.RedisTemplateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.Calendar;

/**
 * @author gaoxi
 * @title: test
 * @projectName demoone
 * @description: TODO
 * @date 2020/03/26 12:28
 */
@Controller
public class test {

    @Autowired
    private RedisTemplateHandler redisTemplateHandler;


    @RequestMapping("/redis")
    @ResponseBody
    public void redisLock(){
        try {
            boolean lock = redisTemplateHandler.redisSetNX("LOCK", String.valueOf(Calendar.getInstance().getTimeInMillis()), 7L);
            //成功获取锁
            if (lock){
                System.out.println("成功获取锁的线程是:");
                System.out.println("--"+Thread.currentThread().getName());
            }else {
                while (true){
                    System.out.println(Thread.currentThread().getName()+"未获得锁");
                    Thread.sleep(3000);
                    boolean lock1 = redisTemplateHandler.redisCheckNX("LOCK", 7);
                    //执行锁失效,造成死锁
                    if (!lock1){
                        //其实这里不用再次执行redisDelNX方法,redisCheckNX方法中已经执行过了,以防万一
                        redisTemplateHandler.redisDelNX("LOCK"); // 释放执行锁
                        break;
                    }
                }
                redisLock();
            }


        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 第三步 利用Jmeter 模拟多线程并发抢锁

测试结果:

为了方便截图,我是用5个线程测试的,这里只贴出部分结果,首先--http-nio-8082-exec-10线程成功抢到锁,其他线程阻塞,没隔3秒重试验证锁是否存在,如果锁没有过期,则继续等待;如果锁已过期不存在,则开始抢锁。当出现死锁时(我是测试用来制造的死锁),手动释放锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值