RedissonLock源代码分析与锁应用


前言

通常服务端存在定时器业务时,如果分布式部署,多台同时执行会出现重复的数据,为了避免这种情况,可采用分布式锁。本章节讲解分布式锁RedissonLock以及锁的业务级的应用,内容包括两部分:

  1. RedissonLock源代码分析
  2. 锁业务应用
    • 接口方法@注解方式 注入锁
    • 代码行加锁

一、RedissonLock源代码分析

1.1 尝试加锁

主线:org.redisson.RedissonLock#tryLock(long waitTime, long leaseTime, TimeUnit unit)

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
        long time = unit.toMillis(waitTime);//取得最大等待时间
        long current = System.currentTimeMillis();
        //取得当前线程id(判断是否可重入锁的关键)
        long threadId = Thread.currentThread().getId();
        //1.尝试申请锁,返回还剩余的锁过期时间
        Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
        if (ttl == null) {
            return true;//2.ttl 为空,申请锁成功
        } else {
            //3.最大等待时间time <= 申请锁的耗时,则申请锁失败
            time = time - (System.currentTimeMillis() - current);
            if (time <= 0L) {
                this.acquireFailed(waitTime, unit, threadId);
                return false;
            } else {
                current = System.currentTimeMillis();
   /**
     * 4.订阅锁释放事件,并通过await方法阻塞等待锁释放:
     * 基于信息量,当锁被其它资源占用时,当前线程通过 Redis 的 channel 订阅锁的释放事件,
     * 一旦锁释放会发消息通知待等待的线程进行竞争
     */
                RFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);
                //await 方法内部是用CountDownLatch来实现阻塞,
                //获取subscribe异步执行的结果(应用Netty Future)
               if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
                    //await(...)返回false,等待时间已经超出获取锁最大等待时间,
                    //取消订阅并返回获取锁失败
                    if (!subscribeFuture.cancel(false)) {
                        subscribeFuture.onComplete((res, e) -> {
                            if (e == null) {
                                this.unsubscribe(subscribeFuture, threadId);
                            }

                        });
                    }
                    this.acquireFailed(waitTime, unit, threadId);
                    return false;
                } else {
                    //await(...)返回true,进入循环尝试获取锁
                    try {
                        time -= System.currentTimeMillis() - current;
                        if (time <= 0L) {
                            this.acquireFailed(waitTime, unit, threadId);
                            boolean var20 = false;
                            return var20;
                        } else {
                            boolean var16;
                              
                            //5.收到锁释放的信号后,在最大等待时间之内,自旋尝试获取锁
                            do {
                                long currentTime = System.currentTimeMillis();
                                // 再次尝试申请锁
                                ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
                                if (ttl == null) {
                                    var16 = true;
                                    return var16;//成功获取锁则直接返回true结束循环
                                }
                                time -= System.currentTimeMillis() - currentTime;
                                if (time <= 0L) {
                                    this.acquireFailed(waitTime, unit, threadId);
                                    var16 = false;
                                    return var16;
                                }
                                currentTime = System.currentTimeMillis();
                                //6.阻塞等待锁(通过信号量(共享锁)阻塞,等待解锁消息)
                                if (ttl >= 0L && ttl < time) {
                                // 在ttl时间内尝试获取信号量
                                ((RedissonLockEntry)subscribeFuture.getNow())
                                .getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                                } else {
                                // 在time时间内尝试获取信号量
                                    ((RedissonLockEntry)subscribeFuture.getNow()).
                                    getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
                                }
                                time -= System.currentTimeMillis() - currentTime;
                            } while(time > 0L);
                            this.acquireFailed(waitTime, unit, threadId);
                            var16 = false;
                            return var16;
                        }
                    } finally {
                        //7.无论是否获得锁,都要取消订阅解锁消息
                        this.unsubscribe(subscribeFuture, threadId);
                    }
                }
            }
        }
    }

订阅:RFuture subscribe(String entryName, String channelName)
‌发布订阅: 基于信号量的异步订阅机制,订阅指定的Redis频道channel,返回异步Future对象

abstract class PublishSubscribe<E extends PubSubEntry<E>> {

	public RFuture<E> subscribe(String entryName, String channelName) {
	   AsyncSemaphore semaphore = this.service.getSemaphore(new ChannelName(channelName));
	   RPromise<E> newPromise = new RedissonPromise();
	   semaphore.acquire(() -> {
	       if (!newPromise.setUncancellable()) {
	           semaphore.release();
	       } else {
	          ......
	        //创建RedisPubSubListener
	        RedisPubSubListener<Object> listener = this.createListener(channelName,value);
	        //订阅通道channelName、信号semaphore,绑定RedisPubSubListener
	        this.service.subscribe(LongCodec.INSTANCE, channelName, semaphore, 
	        new RedisPubSubListener[]{listener});
	       }
	   });
	   return newPromise;
	}
	
   ......
   //创建监听器,用于监听指定频道的消息和订阅状态
   private RedisPubSubListener<Object> createListener(final String channelName, 
           final E value) {
        RedisPubSubListener<Object> listener = new BaseRedisPubSubListener() {
            public void onMessage(CharSequence channel, Object message) {
                //接收到指定频道的消息时,开始处理消息
                if (channelName.equals(channel.toString())) {
                    PublishSubscribe.this.onMessage(value, (Long)message);
                }
            }
            //当订阅成功时,标记 value 的 Promise 为成功。
            public boolean onStatus(PubSubType type, CharSequence channel) {
                if (!channelName.equals(channel.toString())) {
                    return false;
                } else if (type == PubSubType.SUBSCRIBE) {
                    value.getPromise().trySuccess(value);
                    return true;
                } else {
                    return false;
                }
            }
        };
        return listener;
    }
}

子线:tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId)

private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, 
TimeUnit unit, long threadId) {
        if (leaseTime != -1L) {
            return this.tryLockInnerAsync(waitTime, leaseTime, 
            unit, threadId, RedisCommands.EVAL_LONG);
        } else {
            //tryLockInnerAsync-申请锁,申请锁并返回RFuture
            RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(
            waitTime, this.internalLockLeaseTime, TimeUnit.MILLISECONDS, 
            threadId, RedisCommands.EVAL_LONG);
            
            ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
                if (e == null) {
                    if (ttlRemaining == null) {
                        this.scheduleExpirationRenewal(threadId);
                    }
                }
            });
            return ttlRemainingFuture;
        }
    }

tryLockInnerAsync-尝试异步加锁

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, 
RedisStrictCommand<T> command) {
    internalLockLeaseTime = unit.toMillis(leaseTime);
    /**
     * 通过 EVAL 命令执行 Lua 脚本获取锁,保证了原子性
     */
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
              // 1.如果缓存中的key不存在,则执行 hset 命令(hset key UUID+threadId 1),
              然后通过 pexpire 命令设置锁的过期时间(即锁的租约时间)
              // 返回空值 nil ,表示获取锁成功
              "if (redis.call('exists', KEYS[1]) == 0) then " +
                  "redis.call('hset', KEYS[1], ARGV[2], 1); " +
                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                  "return nil; " +
              "end; " +
               // 如果key已经存在,并且value也匹配,表示是当前线程持有的锁,
               则执行 hincrby 命令,重入次数加1,并且设置失效时间
              "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                  "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                  "return nil; " +
              "end; " +
               //如果key已经存在,但是value不匹配,说明锁已经被其他线程持有,
               通过 pttl 命令获取锁的剩余存活时间并返回,至此获取锁失败
              "return redis.call('pttl', KEYS[1]);",
               //这三个参数分别对应KEYS[1],ARGV[1]和ARGV[2]
               Collections.<Object>singletonList(getName()), 
               internalLockLeaseTime, getLockName(threadId));
}
参数说明:
	KEYS[1]就是Collections.singletonList(getName()),表示分布式锁的key
	ARGV[1]就是internalLockLeaseTime,即锁的租约时间(持有锁的有效时间),默认30s
	ARGV[2]就是getLockName(threadId),是获取锁时set的唯一值 value,即UUID+threadId

scheduleExpirationRenewal-redisson看门狗机制

  • 定期续租 Redisson 分布式锁的过期时间,防止锁因超时而被提前释放。
  • 创建一个定时任务,每隔internalLockLeaseTime / 3 毫秒执行一次;定时任务中调用 renewExpirationAsync(),异步续期锁,并递归继续下一次续期。
private void renewExpiration() {
        ExpirationEntry ee = (ExpirationEntry)EXPIRATION_RENEWAL_MAP.
        get(this.getEntryName());
        if (ee != null) {
            Timeout task = this.commandExecutor.getConnectionManager().
            newTimeout(new TimerTask() {
                public void run(Timeout timeout) throws Exception {
                    ExpirationEntry ent = (ExpirationEntry)RedissonLock.EXPIRATION_RENEWAL_MAP.
                    get(RedissonLock.this.getEntryName());
                    if (ent != null) {
                        Long threadId = ent.getFirstThreadId();
                        if (threadId != null) {
                            // 更新锁过期时间(lua脚本)
                            RFuture<Boolean> future = 
                            RedissonLock.this.renewExpirationAsync(threadId);
                            future.onComplete((res, e) -> {
                                if (e != null) {
                                    RedissonLock.log.error("Can't update lock " + 
                                    RedissonLock.this.getName() + " expiration", 
                                    e);RedissonLock.EXPIRATION_RENEWAL_MAP.
                                    remove(RedissonLock.this.getEntryName());
                                } else {
                                    if (res) {
                                        //递归调用 如果10秒后依然没有解锁,继续更新锁过期时间
                                        RedissonLock.this.renewExpiration();
                                    }
                                }
                            });
                        }
                    }
                }
            }, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
            ee.setTimeout(task);
        }
    }
     
     ............
     /*
     异步续订分布式锁的过期时间,具体如下:
	    使用 Lua 脚本保证操作的原子性;
		检查当前线程是否持有锁(通过 hexists 判断);
		如果持有,则通过 pexpire 延长锁的过期时间;
		返回值表示是否成功续订(1 成功,0 失败)
     */
     protected RFuture<Boolean> renewExpirationAsync(long threadId) {
        return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE, 
        RedisCommands.EVAL_BOOLEAN, 
        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
            "redis.call('pexpire', KEYS[1], ARGV[1]); " +
            "return 1; " +
        "end; " +
        "return 0;", 
        Collections.singletonList(this.getName()), 
        this.internalLockLeaseTime, this.getLockName(threadId));
    }
    ............
参数:
this.internalLockLeaseTime = commandExecutor.getConnectionManager()
.getCfg().getLockWatchdogTimeout();//默认30秒

2.2 解锁

org.redisson.RedissonLock#unlock()

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
    /**
     * 通过 EVAL 命令执行 Lua 脚本获取锁,保证了原子性
     */
    return commandExecutor.evalWriteAsync(getName(), 
    LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
   //如果分布式锁存在,但是value不匹配,表示锁已经被其他线程占用,
   //当前客户端线程没有持有锁,不能主动解锁
    "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
         "return nil;" +
      "end; " +
     //如果value匹配,将value减1,主要用在重入锁
     "local counter=redis.call('hincrby',KEYS[1], ARGV[3],-1);" +
     //重入次数减1后的值如果大于0,表示分布式锁有重入过,
     //那么只能更新失效时间,还不能删除
     "if (counter > 0) then " +
          "redis.call('pexpire', KEYS[1], ARGV[2]); " +
          "return 0; " +
      "else " +
       //重入次数减1后的值如果为0,这时就可以删除这个KEY,
       //并发布解锁消息,返回1
          "redis.call('del', KEYS[1]); " +
          "redis.call('publish', KEYS[2], ARGV[1]); " +
          "return 1; "+
      "end; " +
      "return nil;",
       Arrays.<Object>asList(getName(), getChannelName()), 
       LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, 
       getLockName(threadId));
}

参数:
KEYS1:需要加锁的key,这里需要是字符串类型。
KEYS2:redis消息的ChannelName,一个分布式锁对应唯一的一个 channelName:“redisson_lock__channel__{” + getName() + “}”
ARGV1:redis消息体,这里只需要一个字节的标记就可以,主要标记redis的key已经解锁,再结合redis的Subscribe,能唤醒其他订阅解锁消息的客户端线程申请锁。
ARGV2:锁的超时时间,防止死锁
ARGV3 :锁的唯一标识,也就是刚才介绍的 id(UUID.randomUUID()) + “:” + threadId

删除过期信息

void cancelExpirationRenewal() {
    Timeout task = expirationRenewalMap.remove(getEntryName());
    if (task != null) {
        task.cancel();
    }
}

二、锁业务应用

1.服务层方法@注解方式 注入锁

1.1 定义DistributedLock @注解类

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {
    String key();
    int timeout() default 10; // 默认超时时间10秒
    int maxExecuteTime() default 30;
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

1.2 定义DistributedLockAspect 切片类

  拦截带有 @DistributedLock 注解的方法。
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class DistributedLockAspect {

    @Resource
    private RedissonClient redissonClient;

    private final ExpressionParser parser = new SpelExpressionParser();
    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {

        String methodName = joinPoint.getSignature().getName();
        String dynamicKey = parseSpEL(joinPoint, distributedLock.key());
        long timeout = distributedLock.timeout();
        long maxExecuteTime = distributedLock.maxExecuteTime();
        TimeUnit timeUnit = distributedLock.timeUnit();
        //解析key

        log.info("[锁切面] 方法 {} 尝试获取锁,key={}, timeout={}{}", methodName, dynamicKey, timeout, timeUnit.name());

        RLock lock = redissonClient.getLock(dynamicKey);
        boolean isLocked = false;

        try {
            // 尝试获取锁
            isLocked = lock.tryLock(timeout, maxExecuteTime, timeUnit);
            if (!isLocked) {
                log.error("[Redisson锁] 获取锁失败,key={}", dynamicKey);
                throw new RuntimeException("系统繁忙,请重试");
            }

            log.info("[Redisson锁] 锁获取成功,key={}", dynamicKey);
            return joinPoint.proceed(); // 执行业务方法

        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("[Redisson锁] 锁已释放,key={}", dynamicKey);
            }
        }
    }

    /**
     * 解析 SpEL 表达式
     */
    private String parseSpEL(ProceedingJoinPoint joinPoint, String spEL) {
        if (!spEL.contains("#")) {
            return spEL;
        }
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Object[] args = joinPoint.getArgs();
        // 创建 SpEL 上下文
        EvaluationContext context = new MethodBasedEvaluationContext(null, method, args, parameterNameDiscoverer);
        // 解析表达式
        Expression expression = parser.parseExpression(spEL);
        return expression.getValue(context, String.class);
    }
}

1.3 尝试获取锁代码片断

@Around("@annotation(distributedLock)")
public Object around(ProceedingJoinPoint joinPoint, 
DistributedLock distributedLock) {
  	 ......
 	 String dynamicKey = parseSpEL(joinPoint, 
 	 distributedLock.key());
  	 ......
  	 RLock lock = redissonClient.getLock(dynamicKey);
     ......
      // 尝试获取锁
      isLocked = lock.tryLock(timeout, maxExecuteTime, timeUnit);
      if (!isLocked) {
         log.error("[Redisson锁] 获取锁失败,key={}", dynamicKey);
      }
      else{
          log.info("[Redisson锁] 锁获取成功,key={}", dynamicKey);
          return joinPoint.proceed(); // 执行业务方法
      }
}

............

package org.redisson;
public class Redisson implements RedissonClient {
	public RLock getLock(String name) {
        return new RedissonLock(this.connectionManager
        .getCommandExecutor(), name);
    }
}

1.4 释放锁代码片断

 @Around("@annotation(distributedLock)")
 public Object around(ProceedingJoinPoint joinPoint, 
 DistributedLock distributedLock) { 
     ............
     finally {
       if (isLocked && lock.isHeldByCurrentThread()) {
            lock.unlock();
            log.info("[Redisson锁] 锁已释放,key={}", dynamicKey);
        }
     }
}

1.5 服务层注入锁@注解

锁定方法、业务,同一时刻点  或 某一时段内 不可重复执行。
//订单job服务实现类OrderJobServiceImpl  
@Service
@RequiredArgsConstructor
@Slf4j
@RefreshScope
public class OrderJobServiceImpl  {
    /**
     *
     * [描述]: 执行订单创建任务
     * 具备分布式锁防止并发执行,锁超时timeout为30秒
     * @date 2025/6/16 14:09
     * @param id
     * @param updateNextExecuteDate
     * @param executeAtDate
     */
    @Override
    @DistributedLock(key = "#id", timeout = 30)
    public void execute(Long id) { 
     ............
     //某一时刻点执行订单创建任务
     ............
}

2.代码行加锁

2.1 pom.xml文件引入redisson、redis

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

2.2 配置RedissonProperties属性类

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
public class RedissonProperties {
    @Value("${spring.redis.host:}")
    private String redisHost;
    @Value("${spring.redis.port:}")
    private String redisPort;
    @Value("${spring.redis.sentinel.nodes:}")
    private String redisSentinelNodes;
    @Value("${spring.redis.cluster.nodes:}")
    private String redisClusterNodes;
    @Value("${spring.redis.sentinel.master:}")
    private String redisMasterName;
    @Value("${spring.redis.password:}")
    private String password;

    private String springRedis = "spring.redis";
    private int timeout = 10000;
    private int connectionPoolSize = 64;
    private int connectionMinimumIdleSize = 10;
    private int slaveConnectionPoolSize = 250;
    private int masterConnectionPoolSize = 250;

    public String[] getRedisSentinelNodes() {
        return redisSentinelNodes.contains(springRedis) ? null : redisSentinelNodes.split(",");
    }
    public String[] getRedisClusterNodes() {
        return redisClusterNodes.contains(springRedis) ? null : redisClusterNodes.split(",");
    }
}

2.3 Redisson装配类

  • 初始化RedissonClient
  • 装配锁Redisson实现类RedissonDistributedLockerServiceImpl,并将实例注入到RedissLockUtil中。
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @desc 装配类
 */
@Configuration
@ConditionalOnClass(Config.class)
public class RedissonAutoConfiguration {
    @Resource
    private RedissonProperties redissonProperties;

    private static final String REDIS_PROTOCOL_PREFIX = "redis://";

    /**
     * @version 哨兵模式自动装配
     */
    private Config redissonMasterSentinel() {
        String[] nodes = redissonProperties.getRedisSentinelNodes();
        if (null == nodes || nodes.length == 0 || "".equals(nodes[0])) {
            return null;
        }
        List<String> clusterNodes = new ArrayList<>();
        for (String node : nodes) {
            clusterNodes.add(REDIS_PROTOCOL_PREFIX + node);
        }
        Config config = new Config();
        config.useSentinelServers().addSentinelAddress(clusterNodes.toArray(new String[0]))
                .setMasterName(redissonProperties.getRedisMasterName())
                .setTimeout(redissonProperties.getTimeout())
				.setPassword(redissonProperties.getPassword())
                .setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
                .setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());
        return config;
    }

    /**
     * @version 集群模式自动装配
     */
    private Config redissonClusterSentinel() {
        String[] nodes = redissonProperties.getRedisClusterNodes();
        if (null == nodes || nodes.length == 0 || "".equals(nodes[0])) {
            return null;
        }
        List<String> clusterNodes = new ArrayList<>();
        for (String node : nodes) {
            clusterNodes.add(REDIS_PROTOCOL_PREFIX + node);
        }
        Config config = new Config();
        config.useClusterServers()
                .setScanInterval(1000)
                .setTimeout(redissonProperties.getTimeout())
				.setPassword(redissonProperties.getPassword())
                .addNodeAddress(clusterNodes.toArray(new String[0]))
                .setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
                .setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());
        return config;
    }

    /**
     * 单机模式自动装配
     * @return
     */

    private Config redissonAddressSingle() {
        Config config = new Config();
        if (null == redissonProperties.getRedisHost() || "".equals(redissonProperties.getRedisHost())) {
            return null;
        }
        config.useSingleServer()
                .setAddress(REDIS_PROTOCOL_PREFIX+redissonProperties.getRedisHost() + ":" + redissonProperties.getRedisPort())
                .setTimeout(redissonProperties.getTimeout())
				.setPassword(redissonProperties.getPassword())
                .setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
                .setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());
        return config;
    }
    /**
     * 初始化RedissonClient
     * 采用实例化顺序:集群模式 > 哨兵模式 > 单机模式
     */
     @Bean
    RedissonClient redissonSingle() {
        Config config = redissonClusterSentinel();
        if (null == config) {
            config = redissonMasterSentinel();
            if (null == config) {
                config = redissonAddressSingle();
            }
        }
        if (null != config) {
            return Redisson.create(config);
        }
        return null;
    }

    /**
     * @version 装配RedissonDistributedLockerServiceImpl类,并将实例注入到RedissLockUtil
     */
    @Bean
    DistributedLockerService distributedLocker(RedissonClient redissonClient) {
        RedissonDistributedLockerServiceImpl locker = new RedissonDistributedLockerServiceImpl();
        locker.setRedissonClient(redissonClient);
        RedissLockUtil.setLocker(locker);
        return locker;
    }
}

2.4 Redisson实现类和工具类

实现类RedissonDistributedLockerServiceImpl

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
 * @version Redisson实现类
 */

@Service
public class RedissonDistributedLockerServiceImpl 
implements DistributedLockerService {
    private RedissonClient redissonClient;

    public void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    private static final String LOCK = "redisson_lock_";

    @Override
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(LOCK + lockKey);
        lock.lock();
        return lock;
    }

    @Override
    public RLock lock(String lockKey, int leaseTime) {
        RLock lock = redissonClient.getLock(LOCK + lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
        return lock;
    }

    @Override
    public RLock lock(String lockKey, TimeUnit unit, int timeout) {
        RLock lock = redissonClient.getLock(LOCK + lockKey);
        lock.lock(timeout, unit);
        return lock;
    }

    @Override
    public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(LOCK + lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

    @Override
    public boolean isLocked(String lockKey) {
        RLock lock = redissonClient.getLock(LOCK + lockKey);
        return lock.isLocked();
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(LOCK + lockKey);
        if (null != lock) {
            lock.unlock();
        }
    }
    @Override
    public void unlock(RLock lock) {
        lock.unlock();
    }
}

2.5 Redisson工具类RedissLockUtil

import org.redisson.api.RLock;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

/**
 * @desc 工具类
 */
@Component
public class RedissLockUtil {
    private static DistributedLockerService distributedLockerService;
    public static void setLocker(DistributedLockerService locker) {
        distributedLockerService = locker;
    }

    /**
     * 加锁
     * @param lockKey
     * @return
     */
    public static RLock lock(String lockKey) {
        return distributedLockerService.lock(lockKey);
    }

    /**
     * 释放锁
     * @param lockKey
     */
    public static void unlock(String lockKey) {
        distributedLockerService.unlock(lockKey);
    }

    /**
     * 释放锁
     * @param lock
     */
    public static void unlock(RLock lock) {
        distributedLockerService.unlock(lock);
    }

    /**
     * 带超时的锁
     * @param lockKey
     * @param timeout 超时时间   单位:秒
     */
    public static RLock lock(String lockKey, int timeout) {
        return distributedLockerService.lock(lockKey, timeout);
    }

    /**
     * 带超时的锁
     * @param lockKey
     * @param unit    时间单位
     * @param timeout 超时时间
     */
    public static RLock lock(String lockKey, TimeUnit unit, int timeout) {
        return distributedLockerService.lock(lockKey, unit, timeout);
    }

    /**
     * 尝试获取锁
     * @param lockKey
     * @param waitTime  最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @return
     */
    public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
        return distributedLockerService.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);
    }

    public static boolean isLocked(String lockKey) {
        return distributedLockerService.isLocked(lockKey);
    }

    /**
     * 尝试获取锁
     * @param lockKey
     * @param unit      时间单位
     * @param waitTime  最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @return
     */
    public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        return distributedLockerService.tryLock(lockKey, unit, waitTime, leaseTime);
    }

}

2.6 方法体内使用锁

方法体内使用锁,锁定某段区间代码 间的业务。例如下述业务:挂起订单

   @Transactional(rollbackFor = Exception.class)
   public void hangUpOrder(String orderId) {
       Order order = orderMapper.findByOrderId(orderId);
       .......
       try {
           //加锁
           RedissLockUtil.lock(orderRole.getOrderUuid());
           Date time = new Date();
           if ("启用".equals(order.getOrderHangUpStatus())) {
               // 当前是非挂起状态,进行挂起操作.....
               .......
	       }
       } catch (Exception e) {
           log.error("挂起操作有误:{}", e.getMessage(), e);
       } finally {
           //释放锁
           RedissLockUtil.unlock(orderRole.getOrderUuid());
       }
   }

场景一:使用tryLock获取Redis锁。
调用tryLock方法来尝试获取锁。如果成功获得锁,则在持有锁时执行try块中的代码。如果无法获取锁,则执行else块中的代码。

RLock lock = redisson.getLock("myLock");
//lock.tryLock(10, TimeUnit.SECONDS);等待10秒钟,未指定leaseTime参数,
开启看门狗(watchdog)机制自动续约
//lock.tryLock(1L, 10L, TimeUnit.SECONDS);等待1秒钟,指定leaseTime参数10秒(锁从持有到自动释放的时长),watchdog机制不会自动续约
if (lock.tryLock()) {
    try {
        // Do some work while holding the lock
    } finally {
        lock.unlock();
    }
} else {
    // Failed to acquire the lock
}

场景二:使用lock获取Redis锁。
调用lock方法来获取锁。如果不能立即获得锁,代码将阻塞,直到锁可用为止。获取锁后,将执行try块中的代码,同时持有锁。最后,在Finally块中释放锁。

RLock lock = redisson.getLock("myLock");
long leaseTime = 10L;
//指定leaseTime参数10秒(锁从持有到自动释放的时长)
//lock.lock(leaseTime, TimeUnit.SECONDS);
lock.lock();
try {
    // Do some work while holding the lock
} finally {
    lock.unlock();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值