方式1: 


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


比较简洁的一个方式
@Slf4j
@Component
public class RedisDistributedLock {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private static   String PREFIX="rlk_";
    /**
     *  加锁 , redis版本升级2.1以上
     **/
    public Boolean getLock(String key,String value,Long lockExpireSec){
        if(StringUtils.isBlank(key)){ return  false;}
        // key序列化 // value序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));
        return this.redisTemplate.opsForValue().setIfAbsent(PREFIX+key,value, Duration.ofSeconds(lockExpireSec));
    }

    /**
     * 获取一个redis分布锁
     * @param lockKey        锁住的key
     * @param value        锁住的value,  解锁时,value 必须一致
     * @param acquireTimeoutMils 获取超时时间
     * @param lockExpireSec 锁住的时长。如果超时未解锁,视为加锁线程死亡,其他线程可夺取锁
     * @return
     */
    public boolean getLock(String lockKey, String value,long acquireTimeoutMils, long lockExpireSec)  {
        long now = System.currentTimeMillis();
        Boolean isLock = false;
        while (true){
             isLock = getLock(lockKey, value, lockExpireSec);
             if(isLock==true){
               return true;
             }
            if(System.currentTimeMillis()>=now+acquireTimeoutMils)
            {
                //获取超时
                return  false;
            }
            try {
                Thread.sleep(10l);
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }

    /**
     *  释放锁
     **/
    public Boolean releaseLock(String key,String value){
        if(StringUtils.isBlank(key)){return false;}
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript,Long.class);
//        Long releaseStatus = (Long)this.redisTemplate.execute(redisScript,new StringRedisSerializer(), new FastJsonRedisSerializer(Long.class), Collections.singletonList(PREFIX+key),value);
        Long releaseStatus = (Long)this.redisTemplate.execute(redisScript,Collections.singletonList(PREFIX+key),value);
        return releaseStatus==1L;
    }

    /**
     * 删除锁
     * @param key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

}

 








使用 if(getClock()){ todo()}else(todo_other())

获取一个redis分布锁
package com.sea.utils;

import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Objects;
/**
 * *************************************************************************
 * <PRE>
 *  @ClassName:    : RedisDistributedLock
 *
 *  @Creation Date   : Jan 28, 2021 6:47:03 PM
 *
 *  @Author          :  Sea
 *
 * </PRE>
 **************************************************************************
 */
@Component
public class RedisDistributedLock{
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取一个redis分布锁
     *@param nowValue : 枷锁是的唯一值,只有key-value 匹配才可以删除锁 UUID
     * @param lockKey        锁住的key
     * @param lockExpireSec 锁住的时长。如果超时未解锁,视为加锁线程死亡,其他线程可夺取锁
     * @return  (如果锁住时间比较长,一定要手动释放锁)
     */
    public boolean setLock(String lockKey, String nowValue , long lockExpireSec) {
        return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
            Boolean acquire = connection.setNX(lockKey.getBytes(), nowValue.getBytes());
            if (acquire)
            {
                connection.expire(lockKey.getBytes(), lockExpireSec);
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        });
    }

    /**
     * 获取一个redis分布锁
     * @param lockKey        锁住的key
     * @param lockExpireMils 锁住的时长。如果超时未解锁,视为加锁线程死亡,其他线程可夺取锁
     * @return  (不用释放锁,用与短暂锁住)
     */
    public boolean setlock(String lockKey, long lockExpireMils) {
          return (Boolean) redisTemplate.execute((RedisCallback) connection ->
          {
              long nowTime = System.currentTimeMillis();
              Boolean acquire = connection.setNX(lockKey.getBytes(), String.valueOf(nowTime + lockExpireMils + 1).getBytes());
              if (acquire) {
                  return Boolean.TRUE;
              } else
              {
                  byte[] value = connection.get(lockKey.getBytes());
                  if (Objects.nonNull(value) && value.length > 0) {
                      long oldTime = Long.parseLong(new String(value));
                      if (oldTime < nowTime) {
                          //connection.getSet:返回这个key的旧值并设置新值。
                          byte[] oldValue = connection.getSet(lockKey.getBytes(), String.valueOf(nowTime + lockExpireMils + 1).getBytes());
                          //当key不存时会返回空,表示key不存在或者已在管道中使用
                          return oldValue == null ? false : Long.parseLong(new String(oldValue)) < nowTime;
                      }
                  }
              }
              return Boolean.FALSE;
          });
    }
    /**
     * 释放锁
     * @param key
     * @param value
     * @return
     */
    public boolean releaseLock(String key, String value) {
        String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(lua, Long.class);
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
        return 1L == result;
    }

}

 

删除锁:(建议使用方式2的删除) // 释放锁的时候,有可能因为持锁之后方法执行时间大于锁的有效期,此时有可能已经被另外一个线程持有锁,所以不能直接删除 

/**
     * redis 解锁:eval函数在redis集群环境中不支持, 具体查看spring源码
     * @See JedisClusterScriptingCommands
     *
     * Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
     * @param lockName
     * @param uniqueId
     */
    public void unlock(String lockName, String uniqueId) {
        String lockKey = LOCK_PREFIX+lockName;
        String successMsg = StrUtil.format("{}-{}解锁成功[Redis]!",lockName,uniqueId);
        String failMsg = StrUtil.format("{}-{}解锁失败[Redis]!",lockName,uniqueId);
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Boolean> redisScript = new DefaultRedisScript(script,Boolean.class);
        Boolean result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new FastJsonRedisSerializer(Boolean.class), Collections.singletonList(lockKey),uniqueId);
        printResult(result, successMsg, failMsg);
    }

 

 

 /**
     * 释放锁
     * @param key
     * @param value
     * @return
     */
    public boolean releaseLock(String key, String value) {
        String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(lua, Long.class);
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
        return 1L == result;
    }

 

 

 

 

-----------方式2---------------------------------------------

原文:https://blog.csdn.net/qq_28397259/article/details/80839072

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

 

 

package com.icil.bx.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
/**
 * *************************************************************************
 * <PRE>
 *  @ClassName:    : RedisDistributedLock
 *
 *  @Description:    :
 *
 *  @Creation Date   : Jan 28, 2021 6:47:03 PM
 *
 *  @Author          :  Sea
 *
 *
 * </PRE>
 **************************************************************************
 */
@Component
public class RedisDistributedLock{

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    public static final String UNLOCK_LUA;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }

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

    /**
     * edis.set(key,value,"NX","EX",timeOut)【保证加锁的原子操作】
     * key就是redis的key值作为锁的标识,value在这里作为客户端的标识,只有key-value都比配才有删除锁的权利【保证安全性】
     * @param key
     * @param value
     * @param expire
     * @return
     */
    public boolean setLock(String key,String value, long expire) {//1000 * 60
        try {
            RedisCallback<String> callback = (connection) -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                return commands.set(key, value, "NX", "PX", expire);
            };
            String result = redisTemplate.execute(callback);

            return !StringUtils.isEmpty(result);
        } catch (Exception e) {
            logger.error("set redis occured an exception", e);
        }
        return false;
    }

    public String get(String key) {
        try {
            RedisCallback<String> callback = (connection) -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                return commands.get(key);
            };
            String result = redisTemplate.execute(callback);
            return result;
        } catch (Exception e) {
            logger.error("get redis occured an exception", e);
        }
        return "";
    }

    /**
     *
     * @param key
     * @param value:枷锁时候的值  value在这里作为客户端的标识,只有key-value都比配才有删除锁的权利【保证安全性】
     * @return
     */
    public boolean releaseLock(String key,String value) {
        //使用Lua脚本:先判断是否是自己设置的锁,再执行删除
        // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
        // spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本
        try {
            List<String> keys = Arrays.asList(key);
            List<String> args = Arrays.asList(value);
            RedisCallback<Long> callback = (connection) -> {
                Object nativeConnection = connection.getNativeConnection();
                // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
                // 集群模式
                if (nativeConnection instanceof JedisCluster) {
                    return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
                }

                // 单机模式
                else if (nativeConnection instanceof Jedis) {
                    return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
                }
                return 0L;
            };
            Long result = redisTemplate.execute(callback);

            return result != null && result > 0;
        } catch (Exception e) {
            logger.error("release lock occured an exception", e);
        } finally {
            // 清除掉ThreadLocal中的数据,避免内存溢出
//            lockFlag.remove();
        }
        return false;
    }

}

 

posted on 2021-01-28 18:33  lshan  阅读(3021)  评论(0编辑  收藏  举报