如何正确地使用分布式锁?

154 阅读2分钟

 如今,大家基本都用redission来作为分布式锁的实现方式了。以前的set nx ex方式已经很少见了。

那如何使用redission来做分布式锁呢?

传统使用方式如下:

RLock lock = lockService.getLock(key);
boolean isLocked = false;
try {
  isLocked = lock.tryLock();
  if (!isLocked) {
       log.error("加锁失败 key:{}", key);
       return null;
  }
  //do biz logic
}
finally {
  if(isLocked && lock.isHeldByCurrentThread()){
    lock.unlock();
 }
}

有没有更优雅的方式呢?当然有。见如下代码块

/**
     * 同步获取锁,拿不到锁则一直等待
     * @param key
     * @param lockHandler 接入方可以通过匿名类实现业务逻辑
     */
    public void lock(String key, LockHandler lockHandler) {
        RLock lock = getLock(key);
        lock.lock();
        try {
            lockHandler.invoke();
        }
        finally {
            //释放锁
            lock.unlock();
        }
    }

其中,LockHandler 就是一个函数式接口(java8出现)。我们可以在此接口的实现类里写对应的业务逻辑即可。

@FunctionalInterface
public interface LockHandler {
    /**
     * 获取到锁以后的逻辑
     */
    void invoke();
}

如此一来,我们使用分布式锁时,只需要关注业务代码即可。

然而,使用时,我们还要考虑具体的业务场景。比如什么时候用lock方法,什么时候用tryLock方法。而且请注意:tryLock 里面有两个时间参数。代码实现如下:

 /**
     *
     * @param key 锁对应的key
     * @param waitTime  阻塞时间
     * @param leaseTime  锁持有时间 (推荐两个时间都传递)
     * @param timeUnit 时间单位
     * @param lockHandler  函数式接口实现
     * @return 是否获取了锁
     */
    public boolean tryLock(String key, long waitTime, long leaseTime,TimeUnit timeUnit, LockHandler lockHandler) {
        RLock lock = getLock(key);
        // 用于标识是否获取了锁
        boolean acquire = false;
        try {
            // 成功获得锁后返回 true
            if (waitTime <= 0L) {
                acquire = lock.tryLock();
            }
            else {
                acquire = lock.tryLock(waitTime, leaseTime,timeUnit);
            }
            if (acquire) {
                //获得锁后回调具体的业务逻辑
                lockHandler.invoke();
                return true;
            }
            // 没有获得锁
            return false;
        } catch (InterruptedException e) {
            log.error("获取分布式锁失败:{}",e);
            //没有获得锁
            return false;
        }
        finally {
            if (acquire && lock.isHeldByCurrentThread()) {
                // 释放锁
                lock.unlock();
            }
        }
    }

其中waitTime是等待锁的时间,而leaseTime表示锁的持有时间。

大家可以根据业务场景选择不同的方式,并配置好相应的时间参数。

分布式锁的功能可以作为框架的一个组件单独实现,这样业务方只需接入这个SDK就可以了。

具体框架组件的建设步骤,可以关注我的专栏:框架的建设思路和组织结构-CSDN博客