redission.trylock方法

img

为什么不支持高并发呢,是我写的有问题吗?目前看来这个锁是没有办法锁住的

你的 key 是由什么组成?获得锁的结果是什么?

  • 这篇博客: Redis分布式实现原理中的 tryLock方法 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
    	long now = System.currentTimeMillis();
    	// 尝试拿取本地锁
    	if (!this.localLock.tryLock(time, unit)) {
    		return false;
    	} else {
    		try {
    			long expire = now + TimeUnit.MILLISECONDS.convert(time, unit);
    
    			boolean acquired;
    			// 当前时间还没过期并且还未获得redis锁,睡眠100ms继续重试
    			while(!(acquired = this.obtainLock()) && System.currentTimeMillis() < expire) {
    				Thread.sleep(100L);
    			}
    
    			if (!acquired) {
    				this.localLock.unlock();
    			}
    
    			return acquired;
    		} catch (Exception var9) {
    			this.localLock.unlock();
    			this.rethrowAsLockException(var9);
    			return false;
    		}
    	}
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JgfSjKEd-1649006264961)(https://img2022.cnblogs.com/blog/1453965/202204/1453965-20220403172827774-1280476727.gif “点击并拖拽以移动”)]

  • 以下回答由chatgpt基于相关博客总结生成:

    问题分析: 根据参考资料,RedissonLock的tryLock方法可以保证分布式环境下对共享资源的同步访问,但是当使用tryLock方法时,可能出现并发访问的问题,导致无法锁住资源。问题可能是RedissonLock的tryLock方法本身存在问题,也有可能是代码实现错误。

    解决方案: 1.检查代码实现,确保RedissonLock的tryLock方法被正确调用,并且锁的key值没有出现错误。 2.确认Redis版本是否支持分布式锁功能。 3.使用带唯一标识符的Redis分布式锁实现方法,可以通过UUID来为锁增加一个唯一标志。 4.使用定时器实现锁失效时间的续订,防止多线程访问时过期时间的冲突,并保证锁的正确性。 5.在判断获取锁的过程中,加入判断锁是否已经释放,以免锁已经被其它线程或进程释放而导致获取到不正确的锁状态。 6.对锁设置过期时间,并且在调用过程中加入阻塞,等待锁被释放之后再继续执行代码,以保证锁的使用正确性。

    代码示例:

    String uniqueId = UUID.randomUUID.toString() + threadId;
    try{
        String lockKey = "lockKey";
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,uniqueId, "hello",10,TimeUnit.SECONDS);
    
        if(!result){
            return "something exception";
        }
            // 大量业务代码使用锁之后需要将锁释放释放锁的代码如下
            ...
    }
    finally{ 
        if(uniqueId.equals(redisTemplate.opsForValue().get(lockKey))){
            redisTemplate. delete(lockKey);
        }
    }
    // 定义定时器30秒后对锁进行一次刷新防止锁被竞争者抢占 
    private static final ConcurrentMap<String, Timeout> expirationRenewalMap = PlatformDependent.newConcurrentHashMap();  
    private static long interval = 30000;
    
    private void scheduleExpirationRenewal() {
            // 保证任务不会被重复创建
            if (expirationRenewalMap.containsKey(getName())) {
                return;
            }
            // 添加一个netty的Timeout回调任务interval / 3毫秒执行一次
            Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
                @Override
                public void run(Timeout timeout) throws Exception {
                // 异步调用redis的pexpire命令重置过期时间
                    expireAsync(lockExpirationTime, TimeUnit.MILLISECONDS);
                    // 移除确保下一次调用
                    expirationRenewalMap.remove(getName());
                    // 再次循环调用
                    scheduleExpirationRenewal(); 
                }
            }, interval / 3, TimeUnit.MILLISECONDS);
            // expirationRenewalMap如果已经有getName()任务停止任务确保不会重复创建
            if (expirationRenewalMap.putIfAbsent(getName(), task) != null) {
                task.cancel();
            }
        }