
- 保证一个资源在同一时间只能由一个线程访问或由特定类型的线程访问,即一定程度的互斥性
- 互斥性: 同一时刻只能有一个线程持有锁
- 可重入性: 同一节点上的同一个线程如果获取了锁之后能够再次获取锁
- 锁超时:和J.U.C中的锁一样支持锁超时,防止死锁
- 高性能和高可用: 加锁和解锁需要高效,同时也需要保证高可用,防止分布式锁失效
- 阻塞和非阻塞性:能够及时从阻塞状态中被唤醒
- 可重入锁(eg:java中ReentrantLock)
任意线程在获取到锁之后能够再次获取该锁,而不会被该锁所阻塞,该特性的实现需要解决两个问题:- 1)线程再次获取锁:锁需要去识别获取锁的线程是否是当前占据锁的线程,如果是则再次获取锁成功
- 2)锁的最终释放:线程重复n次获取了该锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取的次数进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数为0时表示锁已经成功释放。
- 读写锁(eg: java中的ReadWriteLock)
读写锁将对一个资源的访问分成了2个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读 *** 作不会发生冲突。 - 乐观锁、悲观锁
乐观锁:认为数据一般情况下的 *** 作不会造成冲突,在数据进行更新提交的时候再判断是否有其他线程争用共享数据,没有就 *** 作就成功了;如果共享数据有争用,产生了冲突,那就在采取其他的补偿措施,比如更新失败
悲观锁:总是认为不去做正确的同步措施就肯定会出现问题,所以先对 *** 作对象加锁在进行 *** 作。 - 可中断锁
可以中断的锁。如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,此时可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。Lock接口中的 lockInterruptibly()方法 就体现了Lock的可中断性。
在Java中,synchronized就不是可中断锁,而Lock是可中断锁。 - 公平锁、非公平锁
公平锁:尽量以请求锁的顺序来获取锁。同时有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该锁。
非公平锁:抢占式获取锁。
java的AQS中有非公平和公平模式的锁的实现。对于ReentrantLock和ReentrantReadWriteLock,默认情况下是非公平锁,但是可以设置为公平锁。synchronized是非公平锁。
synchronized,Lock为java中单机环境下的锁,可以用于单机中需要锁的场景。在分布式部署中,分布式锁更是不可缺少。
实现方式- 基于Redis
- 基于数据库
- 基于zookeeper
- 利用setnx+expire命令
setnx和expire是分开的两步 *** 作,不具有原子性,如果执行完第一条指令应用异常或者重启了,锁将无法过期。 - Lua脚本(包含setnx和expire两条指令) 保证原子性
-
"redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end";}```
- 使用 set key value [EX seconds][PX milliseconds][NX|XX] 命令
通常情况下,value设置具有唯一性,解锁时验证是否当前线程持有锁。
释放锁 -
"return redis.call('del',KEYS[1]) else return 0 end"```
redis单实例故障问题在RedLock、Redisson中有更为高级的实现。核心是通过依次对多个实例加锁来确保主从同步或者单实例故障导致锁异常的问题(redis使用异步方式主从同步),通过给锁延时解决业务执行超时问题等。
基于数据库- 基于数据库实现分布式锁,创建一个表,利用数据库的唯一索引、行锁等特性来实现重入、阻塞、非阻塞等锁的特性,锁的超时时间可以使用定时任务超时的记录
- 业务中最常使用的是乐观锁了,如果是批量更新要注意死锁的处理,因为是没有查询直接update了,可以通过排序等方式尽量避免死锁发生。
- zookeeper节点类型
- 持久节点(PERSISTENT):默认节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在。
- 持久节点顺序节点(PERSISTENT_SEQUENTIAL):创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号
- 临时节点(EPHEMERAL):创建节点的客户端与zookeeper断开连接后,临时节点会被删除
- 临时顺序节点(EPHEMERAL_SEQUENTIAL):在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与Zookeeper断开连接后,临时节点会被删除
- zookeeper分布式锁原理
- 利用zk不能重复创建节点的特性实现
- 获取锁:获取锁时创建临时节点
- 释放锁:客户端主动释放则删除该节点即可,客户端故障断开zk后节点自动删除
- 可以利用zookeeper的临时顺序节点和watch机制来实现阻塞锁
- 基于Redis的分布式锁实现
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)