参考博客:https://www.jb51.net/article/154337.htm
什么是分布式锁?在回答这个问题之前,我们先回答一下什么是锁。
普通的锁,即在单机多线程环境下,当多个线程需要访问同一个变量或代码片段时,被访问的变量或代码片段叫做临界区域,我们需要控制线程一个一个的顺序执行,否则会出现并发问题。
如何控制呢?就是设置一个各个线程都能看的见的标志。然后,每个线程想访问临界区域时,都要先查看标志,如果标志没有被占用,则说明目前没有线程在访问临界区域。如果标志被占用了,则说明目前有线程正在访问临界区域,则当前线程需要等待。
这个标志,就是锁。
在单机多线程的java程序中,我们可以使用堆内存中的变量作为标志,因为多线程是共享堆内存的,堆内存中的变量对于各个线程都是可见的。
讲明白了普通的锁,接下来,我们再看看分布式锁。
在分布式环境下,即多台计算机,每个计算机上会启动jvm执行程序的运行环境下,如果不同计算机上的线程想访问临界区域时,该怎么办呢?
前面普通锁的使用堆内存中的变量的方式肯定不适用了。因为在多机环境下,某台计算机上的堆内存中的变量对于其他计算机上的线程肯定是不可见的。那么,根据锁的本质和原理,我们就要找到另外的对于多机上的线程都可见的标志,以它来作为锁,就可以了。这样的锁,就是分布式锁。
当然,这里只是解释了什么是分布式锁,至于分布式锁该如何实现,其实有多重方式,关键在于要保证锁对多机上的程序是可见的即可。一些常用的实现方式是,使用redis,使用数据库等等。
为什么要使用分布式锁
我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug!
注意这是单机应用,也就是所有的请求都会分配到当前服务器的JVM内部,然后映射为操作系统的线程进行处理!而这个共享变量只是在这个JVM内部的一块内存空间!
分布式锁应该具备哪些条件
在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件:
- 1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
- 2、高可用的获取锁与释放锁;
- 3、高性能的获取锁与释放锁;
- 4、具备可重入特性;
- 5、具备锁失效机制,防止死锁;
- 6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
分布式锁的三种实现方式
目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。
- 基于数据库实现分布式锁;
- 基于缓存(Redis等)实现分布式锁;
- 基于Zookeeper实现分布式锁;
Redis实现分布式锁
String lockkey="lock";
while (!RedisUtil.setIfAbsent(lockkey,true,3,TimeUnit.SECONDS)){
try {
Thread.sleep(500L);
}catch (Exception e){
e.printStackTrace();
}
}
int stock=Integer.parseInt((String) RedisUtil.get("stock"));
if(stock>0){
RedisUtil.set("stock",(stock-1)+"");
System.out.println("去库存成功,库存还有"+(stock-1)+"");
}else {
System.out.println("库存不足");
}
RedisUtil.del(lockkey);
}
Redisson实现分布式锁
String lockkey="lockRedisson";
RLock lock= redisson.getLock(lockkey);
try {
lock.lock();
int stock=Integer.parseInt((String) RedisUtil.get("stock"));
if(stock>0){
RedisUtil.set("stock",(stock-1)+"");
System.out.println("去库存成功,库存还有"+(stock-1)+"");
}else {
System.out.println("库存不足");
}
}finally {
lock.unlock();
}