浅谈分布式锁

1. 什么是锁?

      在单进程的系统中,当存在多个线程能够同时改变某个变量时,就须要对变量或代码块作同步,使其在修改这种变量时可以线性执行消除并发修改变量。
   而同步的本质是经过锁来实现的。为了实现多个线程在一个时刻同一个代码块只能有一个线程可执行,那么须要在某个地方作个标记,这个标记必须每一个线程都能看到,当标记不存在时能够设置该标记,其他后续线程发现已经有标记了则等待拥有标记的线程结束同步代码块取消标记后再去尝试设置标记。这个标记能够理解为锁。
   不一样地方实现锁的方式也不同,只要能知足全部线程都能看获得标记便可。如java中synchronize是在对象头设置标记,Lock接口的实现类基本上都只是某一个volitile修饰的int型变量其保证每一个线程都能拥有对该int的可见性和原子修改,linux内核中也是利用互斥量或信号量等内存数据作标记。java

2. 什么是分布式锁?

    分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,经常须要协调他们的动做。若是不一样的系统或是同一个系统的不一样主机之间共享了一个或一组资源,那么访问这些资源的时候,每每须要互斥来防止彼此干扰来保证一致性,在这种状况下,便须要使用到分布式锁。linux

3. 分布式锁实现的几种方式(每种方式都具有多种实现方案)
  • 基于数据库实现分布式锁

基于数据库乐观锁实现:
乐观锁一般实现基于数据版本号(version)的记录机制实现的,在修改数据库前获取版本号,修改数据时与获取版本号不一致则抛出异常(修改数据时切记对版本号进行+1)git

  • 基于缓存(redis...)实现分布式锁

使用redis的setnx()、get()、getset()方法,用于分布式锁redis

  1. setnx(lockkey, 当前时间+过时超时时间) ,若是返回1,则获取锁成功;若是返回0则没有获取到锁,转向2。
  2. get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,若是小于当前系统时间,则认为这个锁已经超时,能够容许别的请求从新获取,转向3。
  3. 计算newExpireTime=当前时间+过时超时时间,而后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime。
  4. 判断currentExpireTime与oldExpireTime 是否相等,若是相等,说明当前getset设置成功,获取到了锁。若是不相等,说明这个锁又被别的请求获取走了,那么当前请求能够直接返回失败,或者继续重试。
  5. 在获取到锁以后,当前线程能够开始本身的业务处理,当处理完毕后,比较本身的处理时间和对于锁设置的超时时间,若是小于锁设置的超时时间,则直接执行delete释放锁;若是大于锁设置的超时时间,则不须要再锁进行处理(这样能够避免死锁)。
  • 基于Zookeeper的分布式锁

    利用临时节点与 watch 机制。每一个锁占用一个普通节点 /lock,当须要获取锁时在 /lock 目录下建立一个临时节点,建立成功则表示获取锁成功,失败则 watch/lock 节点,有删除操做后再去争锁。临时节点好处在于当进程挂掉后能自动上锁的节点自动删除即取消锁spring

4. 推荐开源分布式锁lock-spring-boot-starter

    基于redisson实现的spring boot starter分布式锁框架,实现了可重入锁、公平锁、联锁、红锁、读写锁等经常使用锁的方式,并支持集群模式下的redis。数据库

为何推荐此项目?缓存

  1. 我参与项目的开发
  2. 高可用,使用简单
  3. 支持单机模式,集群模式,云托管模式,哨兵模式
  4. 支持参数加锁,对象属性上加锁,支持方法上加锁

引入maven依赖便可使用并发

<dependency>
     <groupId>io.gitee.tooleek</groupId>
     <artifactId>lock-spring-boot-starter</artifactId>
     <version>1.1.0</version>
 </dependency>
相关文章
相关标签/搜索