Java基础内容之分布式锁

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 在单机环境下多线程操作共享数据时候回用到锁的概念,因为是单机可以直接使用jdk提供的锁机制就可以满足。但是在微服务场景下,因为是多服务共享数据,此时jdk提供的锁就不能再使用了。于是乎就有了分布式锁。本文介绍常见的几种可以使用的生产的分布式锁

作者: 西魏陶渊明
博客: https://blog.springlearn.cn/

在单机环境下多线程操作共享数据时候回用到锁的概念,因为是单机可以直接使用jdk提供的锁机制就可以满足。
但是在微服务场景下,因为是多服务共享数据,此时jdk提供的锁就不能再使用了。于是乎就有了分布式锁。
本文介绍常见的几种可以使用的生产的分布式锁

本文面向有开发经验的同学,所以场景就不赘述,直接上干货

一、分布式锁具有的品格

  • 基本的加锁和释放锁
  • 具备锁失效机制,防止死锁
  • 非阻塞机制
  • 高性能和高可用

二、思考一下如何自己实现?

1. db

根据上面提出的要求,发现只要能满足多服务之前通信就能实现。
比如我们可以用mysql就能实现,比如A服务对一个表加锁和释放锁。B服务就会发现表加了锁。此时B就阻塞了。

当然这明显不满足,非阻塞的机制。另外如果要用一个数据库来做锁的场景也太浪费性能了。

2. redis

利用redis命令来实现,如果返回ok说明获取锁。返回nil说明没有获取到锁。

不阻塞,防止死锁,高性能,都满足

set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:设置失效时长,单位秒
PX milliseconds:设置失效时长,单位毫秒
NX:key不存在时设置value,成功返回OK,失败返回(nil)
XX:key存在时设置value,成功返回OK,失败返回(nil)
//对资源加一个锁 key为资源名 value可以为任意 ex为秒 1为过期时间 nx为
127.0.0.1:6379> set ziyuanming 1 ex 1 nx
OK
127.0.0.1:6379> set ziyuanming 1 ex 1 nx
(nil)

3. zookeeper

获取锁

  1. 在Zookeeper当中创建一个持久节点ParentLock。当第一个客户端想要获得锁时,需要在ParentLock这个节点下面创建一个临时顺序节点 Lock1。
  2. Client1查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁。
  3. 如果再有一个客户端 Client2 前来获取锁,则在ParentLock下载再创建一个临时顺序节点Lock2。
    此时Client2发现自己并不是最靠前的就像Lock1注册了一个Watcher,用于监听Lock1节点释放。此时Client2就进入等待状态
  4. Client3,4以此类推

释放锁

  1. Client1释放了锁,此时Zookeeper就讲Lock1移出,并触发了Lock1的Watcher。
  2. Client2一直在监听Lock1的状态,当Lock1节点被删除,Client2里面收到通知获得了锁。

三、现成的解决方案

1. db的方式就不考虑了

实现简单,但是不划算,性能也不是最好的。

2. redis

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.11.0</version>
</dependency>

public class RedLockTester {
    public static void main(String[] args) {
        //连接redis
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        log.info("连接Redis");

        //1.定义锁
        RLock lock = redisson.getLock("myTest001");

        try {
            //尝试加锁的超时时间
            Long timeout = 300L;
            //锁过期时间
            Long expire = 30L;
            //2.获取锁
            if (lock.tryLock(timeout, expire, TimeUnit.MILLISECONDS)) {
                //2.1.获取锁成功的处理
                log.info("加锁成功");
                //...do something
                log.info("使用完毕");
            } else {
                //2.2.获取锁失败的处理
                log.info("加锁失败");
                log.info("其他处理");
            }
        } catch (InterruptedException e) {
            log.error("尝试获取分布式锁失败", e);
        } finally {
            //3.释放锁
            try {
                lock.unlock();
                log.info("锁释放成功");
            } catch (Exception e) {
                //do nothing...
            }
        }

        //关闭连接
        redisson.shutdown();
        log.info("关闭redis连接");
    }
}

通过官方文档能找到实现第三方工具

参考文章

3. zookeeper

Curator

<!-- 对zookeeper的底层api的一些封装 -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
        </dependency>
        <!-- 封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式Barrier -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.12.0</version>
        </dependency>

几乎对所有的JDK锁都实现了,基于Zookeeper的分布式锁。具体使用方法可以自行百度。

  • InterProcessMutex:分布式可重入排它锁
  • InterProcessSemaphoreMutex:分布式排它锁
  • InterProcessReadWriteLock:分布式读写锁
  • InterProcessMultiLock:将多个锁作为单个实体管理的容器
  • InterProcessSemaphoreV2 信号量
  • DistributedBarrier 分布式栅栏
  • DistributedDoubleBarrier 分布式栅栏

最后求关注,求订阅,谢谢你的阅读!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
22天前
|
缓存 Java
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
本文介绍了几种常见的锁机制,包括公平锁与非公平锁、可重入锁与不可重入锁、自旋锁以及读写锁和互斥锁。公平锁按申请顺序分配锁,而非公平锁允许插队。可重入锁允许线程多次获取同一锁,避免死锁。自旋锁通过循环尝试获取锁,减少上下文切换开销。读写锁区分读锁和写锁,提高并发性能。文章还提供了相关代码示例,帮助理解这些锁的实现和使用场景。
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
|
25天前
|
存储 NoSQL Java
Java调度任务如何使用分布式锁保证相同任务在一个周期里只执行一次?
【10月更文挑战第29天】Java调度任务如何使用分布式锁保证相同任务在一个周期里只执行一次?
69 1
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
82 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
2月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
64 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
2月前
|
Java
Java 中锁的主要类型
【10月更文挑战第10天】
|
2月前
|
安全 Java 开发者
java的synchronized有几种加锁方式
Java的 `synchronized`通过上述三种加锁方式,为开发者提供了从粗粒度到细粒度的并发控制能力,满足了不同场景下的线程安全需求。合理选择加锁方式对于提升程序的并发性能和正确性至关重要,开发者应根据实际应用场景的特性和性能要求来决定使用哪种加锁策略。
23 0
|
2月前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
41 0
|
2月前
|
NoSQL Java 数据库
Java分布式锁
Java分布式锁
39 0
|
2月前
|
缓存 Java 数据库
JAVA分布式CAP原则
JAVA分布式CAP原则
65 0
|
12天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####