Redis(3):Spring集成redis及实现分布式锁

1、pom文件

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.com.demo</groupId>
    <artifactId>SpringBootDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--  spring-boot-starter-parent-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>


    <dependencies>
        <!--    spring-boot-starter-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--访问静态资源-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--集成Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.5.6</version>
        </dependency>
        <!-- jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>


        <!--    <dependency>-->
        <!--      <groupId>org.springframework.boot</groupId>-->
        <!--      <artifactId>spring-boot-start-cache</artifactId>-->
        <!--    </dependency>-->

        <!--redis数据序列化-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <!--    slf4j和logj-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>
        <!--    lombook-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.7.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-project-info-reports-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

2、application.properties

# redis集群配置
spring.redis.cluster.nodes=127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
#获取数据超时
spring.redis.soTimeout=2000
#获取数据超时最大尝试次数
spring.redis.maxAttempts=5
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=20
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=20
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=2000

3、JedisClusterConfig

(1)配置JedisCluster(Redis集群) Bean对象

        JedisCluster(节点集合,连接超时时间,获取数据超时时间,最大尝试次数,密码,连接池配置JedisPoolConfig

(2)RedisConnectionFactory,根据JedisCluster的配置(通过spring-boot-starter-data-redis中的实现)能够自动生成RedisConnectionFactory Bean对象

(3)配置RedisTemplate,通过设置RedisConnectionFactory和设置key和value的序列化方法。

package cn.com.demo.redis;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashSet;
import java.util.Set;

@Configuration
public class JedisClusterConfig{
    @Value("${spring.redis.cluster.nodes}")
    private String clusterNodes;

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

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.soTimeout}")
    private int soTimeout;

    @Value("${spring.redis.maxAttempts}")
    private int maxAttempts;

    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.pool.min-idle}")
    private int minIdle;

    @Value("${spring.redis.pool.max-active}")
    private int maxActive;

    @Value("${spring.redis.pool.max-wait}")
    private long maxWait;

    //自动注入,根据JedisCluster中的信息生成
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;


    @Bean
    public JedisCluster getJedisCluster() {
        return new JedisCluster(getNodes(), timeout,soTimeout,maxAttempts,password,poolConfig());
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(){
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        //key
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //设置value 自定义的序列化
        template.setDefaultSerializer(fastJsonRedisSerializer());
        //设置key序列化方式 String序列化
        template.setKeySerializer(stringRedisSerializer);
        return template;
    }

//    @Bean
//    @Override
//    public CacheManager cacheManager() {
//        // 初始化一个RedisCacheWriter
//        RedisCacheWriter cacheManager = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//        // 设置默认过期时间:2 分钟
//        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
//                .entryTtl(Duration.ofMinutes(1))
//                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
//                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer()));
//        RedisCacheManager redisCacheManager = new RedisCacheManager(cacheManager, defaultCacheConfig);
//        return  redisCacheManager;
//    }
//
    public FastJsonRedisSerializer<Object> fastJsonRedisSerializer() {
        //允许反序列化
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.WriteEnumUsingToString,
                SerializerFeature.WriteClassName);
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        fastJsonRedisSerializer.setFastJsonConfig(fastJsonConfig);
        return fastJsonRedisSerializer;
    }


    /**
     * 连接池配置
     * @return
     */
    private JedisPoolConfig poolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);
        config.setMaxTotal(maxActive);
        config.setMaxWaitMillis(maxWait);
        return config;
    }

    /**
     * 获取集群节点数组
     * @return
     */
    private Set<HostAndPort> getNodes() {
        String[] cNodes = clusterNodes.split(",");
        Set<HostAndPort> nodes = new HashSet<HostAndPort>();
        // 分割出集群节点
        String[] hp;
        for (String node : cNodes) {
            hp = node.split(":");
            nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
        }
        return nodes;
    }
}



4、工具类

通过RedisTemplate来执行redis指令

(1)通过RedisTemplate.opsFor****()获取对应具体数据类型(String,List,Hash,Set,Zset及高级数据结构)的操作(指令)集合;

                redisTemplate.expire(key, expireTime, timeUnit); //设置KEY的超时时间

                redisTemplate.keys(pattern);// KEYS [pattern]

                redisTemplate.delete(keys);// DELETE [key]

                redisTemplate.hasKey(key);//判断key是否存在

 (2)通过****Operations<Serializable, Object>来执行一条具体的指令:

        如:

                //对应指令SETNX

                result = operations.setIfAbsent(key, value);

package cn.com.demo.redis;

import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

@Service
public class RedisUtils {

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 执行lua脚本
     * @param redisScript  lua脚本
     * @param keys   key  Key链表与 KEYS[i]对应
     * @param values   value
     * @return
     */
    public Object executeLua(RedisScript redisScript,List<String> keys,Object... values){
        Object result=redisTemplate.execute(redisScript,keys,values);
        return result;
    }

    /**
     * setNx
     * 如果key不存在则插入,存在直接返回false
     * @param key
     * @param value
     * @return
     */
    public boolean setNx(final String key, Object value) {
        boolean result = false;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.setIfAbsent(key, value);
        return result;
    }

    /**
     * getSet
     *  用于设置给定key的值,并返回key的旧值
     * @param key
     * @param value
     * @return
     */
    public Object getSet(final String key, Object value) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.getAndSet(key, value);
        return result;
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        operations.set(key, value);
        result = true;
        return result;
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime, TimeUnit timeUnit) {
        boolean result = false;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        operations.set(key, value);
        redisTemplate.expire(key, expireTime, timeUnit);
        result = true;
        return result;
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public Object get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }

    /**
     * 哈希 添加
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key, hashKey, value);
    }

    /**
     * 哈希获取数据
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(key, hashKey);
    }

    /**
     * 列表添加
     *
     * @param k
     * @param v
     */
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k, v);
    }

    /**
     * 列表获取
     *
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> lRange(String k, long l, long l1) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k, l, l1);
    }

    /**
     * 集合添加
     *
     * @param key
     * @param value
     */
    public void add(String key, Object value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key, value);
    }

    /**
     * 集合获取
     *
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     *
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key, value, scoure);
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(key, scoure, scoure1);
    }
}

5、通过RedisTemplate执行lua脚本

(1)redislua1.lua

if  redis.call('EXISTS',KEYS[1])==1
then
    redis.call('set',KEYS[1],ARGV[2])
else
    redis.call('set',KEYS[1],ARGV[1])
end
return  redis.call('get',KEYS[1])

(2)加载脚本执行

a、通过RedisTemplate.execute()在集群执行lua脚本必须保证所有的key在同一个槽(Redis实例),即lua脚本要在一个实例上运行,否则会报错CROSSSLOT Keys in request don't hash to the same slot

b、要通过 redisScript.setResultType(PersonInfo.class);设置返回值的类型,否则返回为空。

    public void luaTest(){
        //调用lua脚本并执行
        DefaultRedisScript<PersonInfo> redisScript = new DefaultRedisScript<PersonInfo>();
        //lua文件存放在resources目录下的redis文件夹内
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redislua1.lua")));
        //设置返回值的类型,不设置会返回null
        redisScript.setResultType(PersonInfo.class);
        List<String> keys=new ArrayList<>();
        keys.add("LuaPersonInfo");
        PersonInfo defaultPerson=new PersonInfo("DefaultPerson",20,"Shanghai");
        PersonInfo personInfo1=new PersonInfo("Tom",10,"Beijing");
        Object result=redisUtils.executeLua(redisScript,keys,defaultPerson,personInfo1);
        if(result!=null){
            log.info(result.toString());
        }
    }

运行结果:

 6、Redis实现分布式锁一(通过setNX和getSet实现)

package cn.com.demo.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import java.time.LocalTime;

/**
 * 分布式锁的实现
 */
@Service
public class DistributeLockUtil {

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 获取分布式锁
     *
     * @param lockName   锁名称
     * @param expireTime 超时时间(秒)
     * @return
     */
    public Boolean getDistributeLock(String lockName, long expireTime) {
        while(true){
            LocalTime localTime = LocalTime.now();
            localTime=localTime.plusSeconds(expireTime);
            //1、通过setNx来获取锁
            boolean getSuccess = redisUtils.setNx(lockName, localTime);
            if (getSuccess) {
                //2.1、获取锁成功
                return true;
            }
            //2.2获取锁失败
            //3、判断是锁是否被正常持有
            //3.1获取锁的当前拥有者,持有锁的有效时间
            String vailTimeStr=(String)redisUtils.get(lockName);
            //重新获取锁发现已经被释放,重新开始运行
            if(vailTimeStr==null){
                continue;
            }
            LocalTime vailTime =LocalTime.parse(vailTimeStr);
            localTime = LocalTime.now();
            //3.2  判断有效时间是否在当前时间之后,在之后表明锁被其他线程正常持有,在之前说明锁的当前持有者发生了异常
            if (vailTime.isAfter(localTime)) {
                //被其他线程正常持有,当前线程获取锁失败
                return false;
            }
            //4、被其他线程异常持有,当前线程去重新获取,为了防止在当前线程获取的同时,其他线程先获取,使用getAndSet()来
            String oldTimeStr= (String)redisUtils.getSet(lockName,localTime.plusSeconds(expireTime));
            if(vailTimeStr.equals(oldTimeStr)){
                //没有其他线程在当前线程之前获取到锁
                return true;
            }
            //在当前线程获取锁之前,已经有其他线程获取到了锁
//            System.out.println("在当前线程获取锁之前,已经有其他线程获取到了锁");
            return false;
        }
    }

    /**
     * 释放锁
     * @param lockName
     */
    public void releaseDistributeLock(String lockName){
        redisUtils.remove(lockName);
    }
}
    public void doProcess(String lockName,int time) throws InterruptedException {
        while (true){
            if(distributeLockUtil.getDistributeLock(lockName,5)){
               log.info(Thread.currentThread().getName()+"获取锁成功");
                Thread.sleep(time*1000);
                log.info(Thread.currentThread().getName()+"休眠后,释放锁");
                distributeLockUtil.releaseDistributeLock(lockName);
                return;
            }else{
                log.info(Thread.currentThread().getName()+"获取锁失败,短暂休眠后重新请求锁");
                Thread.sleep(1000);
            }
        }
    }

 (1) Key存放锁的名称,Value存放当前锁的过期时间点(自己计算过期时间)

(2)通过String的SETNX指令上锁,返回true上锁成功

(3)返回false表示上锁失败,上锁失败即其他线程正持有锁,此时其他线程可能存在两种情况:第一种持有锁的线程线程正常在执行任务;第二种持有锁的线程(或实例)异常但是没来的及释放锁。通过锁的过期时间点在当前时间之前还是之后来判断(在当前时间之后,认为正在正常运行;在当前时间之前,认为持有锁的线程异常不能及时释放锁)

(4)持有锁的线程正常,返回false表示此次获取锁失败

(5)持有锁的线程异常,则当前线程去强抢锁。去强抢锁时要注意可能有其他的线程此时也来强抢锁,通过cas(Redis的getSet指令)来保证线程强抢锁的线程安全:首先通过get()指令获取到过期时间t1,然后通过getSet将设置t3,同时返回此时的值t2,如果t1=t2,就说明这个过程中没有其他线程来强抢锁,当前线程获取锁成功;如果t1!=t2则说明在当前线程强抢锁之前已经有线程强抢到了锁,当前线程获取锁失败。

(6)删除锁直接通过redis的delete指令,锁只能是持有的线程手动的释放,或不释放请求的线程       强抢

 7、Redis实现分布式锁二(SETNX和lua脚本)

(1)value为持有锁的线程标识,通过redis实现锁的超时过期

(2)锁是被持有线程手动释放的或redis的过期删除策略自动释放

(3)通过setnx上锁,设置过期时间及所持有线程,成功返回true,失败返回false

(4)通过lua脚本释放锁,使用lua脚本是使判断指令和删除指令之间的操作具有原子性

(5)lua脚本:

    redisLock.lua

if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del",KEYS[1])
else
    return 0
end
package cn.com.demo.redis;

import com.sun.org.apache.xpath.internal.operations.Bool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

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

/**
 * 通过SETNX和lua脚本实现
 */
@Service
public class DistributeLockUtil2 {

    public final static String serverInstanceName="DemoServer1_";

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 上锁
     * @param lockName
     * @return
     */
    public Boolean getLock(String lockName,Long time,TimeUnit timeUnit){
        //1.通过setNX获取锁,Value为持有锁的线程名称
        Boolean result=redisUtils.setNxEx(lockName,serverInstanceName+Thread.currentThread().getName(),time, timeUnit);
        if(result){
            //setnx成功表示获取锁成功
            //TODO:添加定时任务去刷新锁的有效时间
        }
        return  result;
    }


    /**
     * 锁的状态
     * (1)锁不存在了,即任务已经超时了,无需释放锁,返回false
     * (2)锁仍然存在:
     *      (2.1)value还是当前的线程,正常运行,手动释放锁,返回true
     *      (2.2)value已经别为了其他线程,当前线程运行超时,锁被redis自动释放,不能释放锁,返回false
     * @param lockName
     * @return
     */
    public Boolean  releaseLock(String lockName){
        //1.获取lua脚本
        DefaultRedisScript<Boolean> redisScript=new DefaultRedisScript<>();
        redisScript.setResultType(Boolean.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redisLock.lua")));
        //2.执行lua脚本(原子),判断当前锁的
        List<String> keys=new ArrayList<>();
        keys.add(lockName);
        Boolean result=(Boolean)redisUtils.executeLua(redisScript,keys,DistributeLockUtil2.serverInstanceName+Thread.currentThread().getName());
        return result;
    }

}
    public void doProcess2(String lockName,int time) throws InterruptedException {
        while (true){
            if(distributeLockUtil2.getLock(lockName,10l, TimeUnit.SECONDS)){
                log.info(Thread.currentThread().getName()+"获取锁成功");
                Thread.sleep(time*1000);
                log.info(Thread.currentThread().getName()+"休眠后,释放锁");
                distributeLockUtil2.releaseLock(lockName);
                return;
            }else{
                log.info(Thread.currentThread().getName()+"获取锁失败,短暂休眠后重新请求锁");
                Thread.sleep(1000);
            }
        }
    }

8、两种实现的区别

(1)释放锁的方式:一种是手动释放加不释放强占;另一种是手动释放加Redis自动释放

(2)超时实现的方式:一种是手动实现,另一种是通过Redis的过期删除

9、相同点:

 存在相同的问题:

a、可能出现,当前持有锁的线程A执行超时,但是此时它仍然是在正常运行的,请求锁的线程B获取到锁,可能会导致业务逻辑出现错误(第二种方法可以通过启动定时任务不断的刷新有效时间来解决)

b、不可重入

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值