分布式锁
为什么需要有分布式锁呢,在单点的时候synchronized 就能解决,但是服务拆分之后,每个服务都是单独的机器,无法解决,所以出现了分布式锁,其实也就是用各种手段,实现获取唯一锁,别人无法得到。
其实在做分布式锁的前提,需要先明白,synchronized 为啥不能使用了,啥原理让他在一个机器上可以使用。
synchronized 的原理
众所周知 Synchronize
关键字是解决并发问题常用解决方案,有以下三种使用方式:
- 同步静态方法,锁的是当前
Class
对象。 - 同步块,锁的是
{}
中的对象。 - 同步普通方法,锁的是当前对象。
实现原理:
JVM
是通过进入、退出对象监视器( Monitor
)来实现对方法、同步块的同步的。
具体实现是在编译之后在同步方法调用前加入一个 monitor.enter
指令,在退出方法和异常处插入 monitor.exit
的指令。
其本质就是对一个对象监视器( Monitor
)进行获取,而这个获取过程具有排他性从而达到了同一时刻只能一个线程访问的目的。
而对于没有获取到锁的线程将会阻塞到方法入口处,直到获取锁的线程 monitor.exit
之后才能尝试继续获取锁。
可以通过使用 javap -c Synchronize
可以查看编译之后的具体信息 ,这里我就不再粘贴了。
所以可以知道,单独的Java虚拟机是可实现锁的,但是多台手就伸不到了,只能在依赖外部的形式去产生一个唯一锁。
以上是参考别人的博客拿到的信息,亲自试用得到,准确 :链接:https://www.jianshu.com/p/2ba154f275ea
Redis实现的分布式锁
主要是利用了redis的set NX的原理,以及对redis的script脚本原子性利用。(个人看法,其实后面一步就看各自的程序逻辑如何去判定到底要不要这一步了)
简单的说一下主要流程:
- 首先设置一个全局唯一的key和一个唯一性的value(value是一个解锁的保障,删除之前判断一下值是否一致)
- 使用Redis的set 方法 以多参数形式配置key,value,nx,px,过期时间 (参数 NX:只有键不存在时,才能对其进行设置操作)
- 利用Redis的script脚本来对key的删除操作,只能自己删除自己的value(删除之前先判断一下value是否是我之前的value,是否被改过,没有就删了)
对其解释: 根据 get 获取到 key 的值 ,判断key的值是否跟你传的值相等,相等则 执行del key 否则返回0 结束
对此推荐去看一下redis的Lua脚本,给个简单的:http://redisdoc.com/script/eval.html
在实际场景中可以利用AOP的切点切面形式,实现具体是什么地方需要分布式锁,搭配分布式锁,再搭配注解方式,来实现想要的自定义设置那里需要就点哪里
AOP的主要实现是
- @pointcut 切注解类
- 在使用@around环绕对前后做处理
- 前面加锁
- 后面解锁del 使用script脚本
代码主要实现
注解类 DistributedLock
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface DistributedLock { /** * 自定义形式添加你对分布式锁的对外配置属性 * 例如:key的规则,锁的超时时间,获取锁的等待时间,等一系列的属性配置 * */ }
切面类 DistributedLockAspect
@Aspect @Component public class DistributedLockAspect { /** * 层切点 */ @Pointcut("@annotation(com.creditease.hardess.common.annotation.DistributedLock)") public void distributedLockAspect() {} /** * @param joinPoint 切点 * @return Object 添加分布式锁后,实际调用业务逻辑部分代码的返回值 * @throws Throwable 产生的所有异常,为了避免对异常处理流程产生干扰,所有异常都应该继续抛出 */ @Around("distributedLockAspect()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { /** *主要写一些锁的获取,和业务逻辑执行,锁的删除等 * */ return returnObject; } /** * 获取 DistributedLock 注解 * * @param joinPoint * @return 代码中定义的注解 * @throws NoSuchMethodException */ private static DistributedLock getDistributedLock(ProceedingJoinPoint joinPoint) throws NoSuchMethodException { String methodName = joinPoint.getSignature().getName(); Class<?> classTarget = joinPoint.getTarget().getClass(); Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); Method objMethod = classTarget.getMethod(methodName, par); return objMethod.getAnnotation(DistributedLock.class); } } }