用 redis 设计 分布式锁 (一)

  • Post author:
  • Post category:其他


项目开发中我们经常需要用到分布式锁,尤其是在集群环境多台服务器情况下,比如 接口请求、定时任务等业务环境中;

今天 我们先介绍一种 redis 分布式锁,在普通项目中就可以使用,代码如下:

1、redis 加锁和释放锁代码如下:

package com.jy.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCommands;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * RedisTemplate工具类  通过注解(@Component)扫描此类 
 */
@Component
public class RedisUtil implements ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);

    private static RedisTemplate<Object, Object> redisTemplate;//redis 模板类

    private static ThreadLocal<String> lockFlag = new ThreadLocal<String>();

    public static final String UNLOCK_LUA;

   //redis 加锁方法
    public static Boolean setNX(String key,long timeout) {
        String  value = String.valueOf(System.currentTimeMillis());//锁的value 值设置
        Boolean isExit = false;
        try {
            //设置锁的核心代码
            isExit = redisTemplate.getConnectionFactory().getConnection().setNX(key.getBytes(), value.getBytes());
            /** 如果设置成功,要设置其过期时间 */
            if (isExit) {
                redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
                logger.info(key+":成功获取到锁");
                return isExit;
            }
        }catch (Exception e){
            logger.info(key+":获取锁失败",e);
        }
        logger.info(key+":未获取到锁");
        return isExit;
    }


//释放锁的核心方法
    public static void releaseLock(String key ){
        try {
            redisTemplate.opsForValue().getOperations().delete(key);//直接删除key
            logger.info(key+";锁释放成功");
        } catch (Exception ex) {
            logger.error(key+";锁释放失败",ex);
        }
    }

     //通过springIOC容器取redis模板类
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        redisTemplate = applicationContext.getBean(RedisTemplate.class);
    }

}

2、redis 分布式锁就这么简单,但是!此种方案是有有坑的,在普通项目、并发量小、定时任务频率低、服务稳定的的情况下勉强可以使用。分析一下原因:首先,该加锁过程不能保证原子性,比如刚刚加锁之后,还没有来得及设置超时时间,服务出问题了,或者中间出现了异常代码,那么这个锁没有设置超时时间,就可能成为死锁(如果业务代码中没有成功释放锁)。

其次:关于释放锁,如果在多线程情况下,可能会把其他线程的锁(同一个key : 锁)释放掉,这对业务来说是非常危险的。

下篇我将介绍另一种redis 分布式锁,能很好的弥补这种方案的漏洞,敬请期待!下篇地址:


https://blog.csdn.net/nandao158/article/details/105866418



版权声明:本文为nandao158原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。