在java中,多线程环境下,需要控制对资源的并发访问,通常会使用synchronized关键字或者ReentrantLock可重入锁,不过在分布式环境下需要新的方法来加锁,才能满足需要。
为什么需要分布式锁?
分布式锁要解决的问题有两个:效率,正确性。
- 效率:用分布式锁可以避免相同的节点做相同的工作,提升效率
- 正确性:防止多个节点对同一个数据同时操作,导致数据的状态不正确。比如多个节点的机器对同一个订单同时进行不同的操作,导致订单的状态最后出错。
分布式锁的特点
- 互斥性:和本地锁类似,分布式锁需要不同节点的不同线程之间互斥,同一时刻只允许一个线程获取锁。
- 可重入性:同一个节点的同一个线程获取了这个锁之后,还可以再次获取这个锁
- 锁超时:类似于本地锁,防止死锁的发生
- 支持阻塞和非阻塞:和ReentrantLock类似,支持lock,trylock,trylock(long timeOut)
常见的分布式锁
常见的分布式锁实现由redis,Zookeeper,MySQL还有一些公司自己研发的比如谷歌Chubby
Redis
redis性能很好,实现起来也简单,所以很多人会选择redis作为分布式锁。
对一个资源resourceName加锁,可以用setNx resourceName value
. 为了防止加锁后机器宕机导致锁永远不会被释放。我们需要加入过期时间,在redis2.8之后,redis支持nx和ex是同一原则操作。可以用以下代码完成
set resourceName value ex 5 nx
Redission:
如果需要实现比较复杂的需求可以考虑使用redission