Redis分布式锁
-
- 分布式锁的实现原理和不同方式的实现对比
- 基于Redis实现的分布式锁
集群架构下的并发问题
在单体架构上,乐观锁和悲观锁可以锁住并发情况下的同步代码块,我们多使用synchronized来对方法加锁。但是在配上负载均衡的集群模式下, 普通的synchronized是无法锁住从两台服务器同时进入的请求 。
这是在了解秒杀项目的难点之一: 一人一单的并发安全问题 在使用集群架构出现的难点。我们先从 单体项目 出发,单体项目很好理解,假如有俩线程:线程1查询订单,判断是否存在。然后第二个线程之后在去查询订单,判断是否存在,在synchronized的作用下两个线程是不会发生问题的。
那现在,我们不再是一台服务器,而是多台。在当前这一个JVM内部,锁的原理是在JVM内部维护了一个 锁监视器对象 ,监视器的对象用的是userId,它是在我们的 常量池 里面。那么在这个JVM内部是维护了这一个常量池子,当ID相同的情况下,他们永远都是同一个锁,也就是说锁的监视器是同一个。所以无论是线程1也好,线程2也好,他们俩要获取锁的时候,锁监视器就会记录线程ID,当另一个线程再来获取锁的时候肯定是不行的,因为锁监视器已经记录一个了。
但是,当我们部署一个新的服务器的时候,也就是部署了一个新的JVM。两个JVM也拥有各自的常量池,JVM2用userId作为锁的时候,它的监视器对象就会拥有一个新的锁监视器,跟JVM1的监视器不是同一个。现在当我们线程3来获取锁的时候走的是自己的监视器,那这个监视器显示的是空的呀,所以也能获取锁成功,当然了线程4失败是没问题的。也就是说在JVM内部锁监视器能保证这些线程互斥,但是多个JVM就会有多个JVM监视器, 有多少个锁监视器就会有多少个线程成功进入同步代码块。
所以我们要解决的问题就是在多个JVM的情况下让这些锁监视器使用同一把锁。
分布式锁的实现原理和不同方式的实现对比
synchronized就是利用JVM内部的锁监视器来控制线程的,在JVM内部,因为只有一个锁监视器,所以只会有一个线程获取到锁,可以实现线程阶段互斥。但是当有多个JVM的时候,就会有多个锁监视器,这时候synchronized就会显得苍白无力,JVM内部的锁监视器直接作废。 所以锁的监视器一定要在JVM的外部 ,让所有JVM都去找独一无二的锁监视器来获取锁,这样也就只有一个线程获取锁,也就实现了多JVM的线程互斥。
<mark>所以满足在分布式系统或集群模式下 多线程可见 并且 互斥 的锁就是分布式锁</mark> 。
分布式锁核心是实现多进程之间的互斥,而满足这一点的方式有很多,常见的有三种:Mysql、Redis。Redis里有 setnx
互斥命令,王redis面set数据的时候,只有没数据的时候才会set成功,有数据就会set失败。
基于Redis实现的分布式锁
实现分布式锁肯定要实现两个基本方法, 获取锁 和 释放锁 。
获取锁
- 互斥条件:确保只能有一个线程获取到锁。
- 非阻塞:尝试一次,成功返回true,失败返回false。
这个我们可以用redis的setnx
,这个可以确保只有一个可以返回1。
确保原子性,利用EX。
释放锁
- 手动释放 del lock
思考问题:如果在获取锁后redis宕机了,那么这个释放锁动作就永远得不到执行,其他线程进不来,我这服务已经挂了,整个线程进入死锁状态。所以需要在获取锁时添加过期时间,避免服务器宕机引起的死锁。
Redis分布式锁1.0
锁的名称不能写死,不同的业务有不同的锁。
欢迎关注我的公众号:敲代码的老贾,回复“领取”赠送《Java面试》资料,阿里,腾讯,字节,美团,饿了么等大厂