在一个项目中,为了保证线程安全,我们可以使用 synchronized 自动锁、Lock 手动锁、线程安全的类(java.util.concurrent 下的类),或者用 volatile 修饰变量。
分布式红包
以支付宝集五福抢红包为例,大年三十的晚上,集齐了五福的人都在访问支付宝的抢红包功能,假设红包总金额为 1 亿元,一共有 10 台服务器。
服务器1:收到小明的请求 → 查看当前总红包金额 → 随机生成小明的红包金额 → 更新当前总红包金额(总红包金额 - 小明的红包金额)。
服务器2:收到小明的请求 → 查看当前总红包金额 → 随机生成小明的红包金额 → 更新当前总红包金额(总红包金额 - 小明的红包金额)。
如何获取当前的总红包金额呢?
- 如果每个服务器都存 1 亿,那总共发出去的红包就有 10 亿了,支付宝赔大了。
- 如果平分,每个服务器存 1 千万,那可能会出现红包发不完的情况。(每个服务器无法判断当前用户是否为最后一个)
- 这就需要一个公共的地方保存总红包金额,各个服务器想要获取和更新它的时候,都要先获取它的分布式锁。
Redis 分布式锁
使用 setnx key value 指令,在系统里占一个锁,返回 1 是成功,返回 0 是失败,执行完成后,使用 del key 释放锁,其它程序才能执行。
如果占有锁的程序还没有执行完成就挂了,会造成死锁,所以要设置过期时间。
Zookeeper 分布式锁
使用 create /key value 创建一个节点(临时顺序性节点,防止死锁,顺序执行),在系统里占一个锁,如果成功了,就会执行,如果失败了,就监听这个节点的变化,执行完成后,删除节点,监听它的程序就会收到通知。