falsecolor
V1
2022/03/15阅读:27主题:全栈蓝
分布式锁
分布式锁
锁
还记得我们学习多线程那会有一个经典的防止超卖的案例吗?双十一商家举行了一场空前的活动,很多商品都参加了秒杀,其中有一个商品只有5件库存,如果让你模拟一场对这个商品的秒杀活动,你会怎么编写程序呢?很多人可能已经胸有成足地想好了,相信有不少人是这样写的。
首先编写商品类
public class Good{
Integer count=5;
/**
* 卖出一件商品,模拟一次用户点击和请求
*/
public void sale(){
if(count>0){
this.count-=1;
System.out.println("卖出一件商品,剩余:"+count);
}
}
public Integer getCount() {
return count;
}
}
再来启动多个线程来模拟秒杀
Good good = new Good();
while (good.getCount()>0){
new Thread(()->{
good.sale();
}).start();
}
System.out.println("抢购结束");
好了,如果此时点击运行,理想中打印的结果是这样的
卖出一件商品,剩余:4
卖出一件商品,剩余:3
卖出一件商品,剩余:2
卖出一件商品,剩余:1
卖出一件商品,剩余:0
抢购结束
可实际上大概率并不是理想中的结果,而是类似这样的
卖出一件商品,剩余:4
卖出一件商品,剩余:3
卖出一件商品,剩余:1
卖出一件商品,剩余:2
卖出一件商品,剩余:4
卖出一件商品,剩余:4
卖出一件商品,剩余:4
卖出一件商品,剩余:-2
卖出一件商品,剩余:-5
卖出一件商品,剩余:-4
抢购结束
卖出一件商品,剩余:-2
怎么解决这个问题,防止出现超卖和类似问题呢?相信大家已经懂了,那便是今天的主角:锁。
Lock和synchronized
在单体流行的时代,对于解决上面的问题,最容易想到的有两种解决方案:
Lock
ReentrantLock lock = new ReentrantLock();
Good good = new Good();
while (good.getCount()>0){
new Thread(()->{
lock.lock();
good.sale();
lock.unlock();
}).start();
}
System.out.println("抢购结束");
synchronized
Object lock = new Object();
Good good = new Good();
while (good.getCount()>0){
new Thread(()->{
synchronized(lock){
good.sale();
}
}).start();
}
System.out.println("抢购结束");
本文的重点是分布式锁,故不再详细讲解Lock和synchronized的用法,感兴趣的同学可以自行进行学习。
分布式锁
微服务越来越流行,上面的两种方式在微服务中已经变的不再适用,试想一下,某服务运行在3台服务器上,第一个请求在A服务器上获得了锁,此时第二个请求进入B服务器,此时它仍然可以获得锁。为了解决Lock和synchronized的局限性,分布式锁便派上用场了。
分布式锁有不同的觉得方案,有用ZooKeeper实现有,有基于ETCD实现的,也有基于Redis实现的,具体采用什么方案,需要综合具体场景以及当前已经使用的技术栈来决定,本文以Redis为例来讲述通用的案例。
基于spring boot starer的实现
Spring Boot 实现 Redis 分布式锁在 spring-integration 这个项目中,所以需要这三个依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
配置
@Bean(destroyMethod = "destroy")
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
return new RedisLockRegistry(redisConnectionFactory, "lock");
}
示例
@Autowired
RedisLockRegistry redisLockRegistry;
@Test
public void test(){
Good good = new Good();
while (good.getCount()>0){
new Thread(()->{
Lock obtain = redisLockRegistry.obtain("lock-key");
try {
boolean b = obtain.tryLock(10, TimeUnit.SECONDS);
if(success){
good.sale();
}
} catch (InterruptedException e) {
e.printStackTrace();
obtain.unlock();
}finally {
obtain.unlock();
}
}).start();
}
System.out.println("抢购结束");
}
基于Redisson的实现
Good good = new Good();
while (good.getCount()>0){
new Thread(()->{
Lock obtain = redisLockClient.getLock("key");
try {
boolean success = obtain.tryLock(10, TimeUnit.SECONDS);
if(success){
good.sale();
}
} catch (InterruptedException e) {
obtain.unlock();
}finally {
obtain.unlock();
}
}).start();
}
System.out.println("抢购结束");
当然,本案例只是讲解分布式锁的使用方法,并不一定完全切合所有的使用场景,例如上诉案例中关于库存量,实际场景中便不能直接存储在内存中,而是使用redis等进行存储
作者介绍
falsecolor
V1