函数式接口实现分布式锁

191 阅读2分钟

函数式接口实现分布式锁

背景

最近使用分布式锁比较多,发现大家使用分布式锁,都各有各的用法,想着用搞一个加锁模板,方便大家使用,就选择声明一个函数式接口,去实现加锁的模板

函数式接口

是一种特殊的接口,只包含一个抽象方法。函数式接口的目的是为了支持函数式编程,使开发者能够以更简洁的方式定义单一抽象方法的接口,从而可以使用Lambda表达式来实现这个接口的抽象方法。可以通过lambda表达式进行传递,简单理解就是java8对函数式编程的一种支持

实战

我们一般加锁是这样,尝试获取锁,失败则抛异常,成功则执行业务代码,每次都要写一大段逻辑

 RLock lock = redissonClient.getLock(lockKey);
        try {
            if (!lock.tryLock(waitTime, leaseTime, unit)) {
                throw new CommonException(ErrorCodeEnum.TRY_LOCK_ERROR).detailMessage("操作频繁,请稍后再试!");
            }
            。。。。业务代码
        }catch (CommonException commonException) {
        } catch (Exception e) {
            log.error("LockTemplate >>  获取锁异常",e);
        } finally {
            try {
                // 锁不为空  是否还是锁定状态  当前执行线程的锁
                if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            } catch (Exception e) {
                log.error("LockTemplate >>  获取锁异常",e);
            }
        }
    }

所以可以采用函数是接口,定义一套加锁模板,开发者就可以只关注业务代码,而不用去写一大段加锁逻辑,

package com.dept.common.template;

import cn.hutool.core.util.StrUtil;
import com.dept.common.enums.ErrorCodeEnum;
import com.dept.common.exception.CommonException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@AllArgsConstructor
@Slf4j
public class LockTemplate {

    private RedissonClient redissonClient;
    public void templateLock(String prefix,String key,Runnable target,long waitTime, long leaseTime, TimeUnit unit) {
        String lockKey = StrUtil.format(prefix,key);
        // 判断是否正在使用
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (!lock.tryLock(waitTime, leaseTime, unit)) {
                throw new CommonException(ErrorCodeEnum.TRY_LOCK_ERROR).detailMessage("操作频繁,请稍后再试!");
            }
			// 执行业务代码
            target.run();
        }catch (CommonException e){
            throw e;
        } catch (Exception e) {
            log.error("LockTemplate >>  获取锁异常",e);
        } finally {
            try {
                // 锁不为空  是否还是锁定状态  当前执行线程的锁
                if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            } catch (Exception e) {
                log.error("LockTemplate >>  获取锁异常",e);
            }
        }
    }
}

重点就是将Runnable做为入参,Runnable是一个函数式接口,所以我们可以直接传入业务实现,当然我们也可以自己定义一个函数式接口

  lockTemplate.templateLock(SystemConstants.STAR_TASK_MINA_ORDER_LOCK, order.getOrderSn(), () -> {
           。。。业务实现
        }, RedisConstants.ONE_SECOND, RedisConstants.TEN_SECOND, TimeUnit.MINUTES);

这样我们就可以不用去关注加锁逻辑,只需要写自己的业务代码接口

总结

通过函数式接口,我们可以定义很多模板代码,让开发者只需要传入对应的业务实现即可,TransactionTemplate的execute的方法也是类似的实现