Redis学习(八)Java三种方式实现分布式锁

这篇具有很好参考价值的文章主要介绍了Redis学习(八)Java三种方式实现分布式锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、背景

在分布式服务中,经常有例如定时任务这样的场景。

在定时任务中,如果不使用 quartz 这样的分布式定时工具,只是简单使用 @Schedule 注解来实现定时任务,在服务分布式部署中,就有可能存在定时任务并发重复执行问题

对于解决以上场景中的问题,我们引入了分布式锁

二、具体实现

1.RedisTemplate 实现(非阻塞)

RedisUtils 工具类:

@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 原子性操作 加锁
     */
    public boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit));
    }

    /**
     * 原子性操作 解锁
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }
}

使用示例:

@Resource
private RedisUtils redisUtils;

public void updateUserWithRedisLock(SysUser sysUser) throws InterruptedException {
    // 1.获取分布式锁
    boolean lockSuccess = RedisUtils.setIfAbsent("SysUserLock" + sysUser.getId(), "value", 30, TimeUnit.SECONDS);
    if (lockSeccess) {
        // 加锁成功...
        // TODO: 业务代码

        // 释放锁
        redisUtils.delete("SysUserLock" + sysUser.getId());
    } else {
        // 如果需要阻塞的话,就睡一段时间再重试
        Thread.sleep(100);
        updateUserWithRedisLock(sysUser);
    }
}

setIfAbsent() 方法的作用就是在 lock key 不存在的时候,才会设置值并返回 true;如果这个 key 已经存在了就返回 false,即获取锁失败。


2.RedisLockRegistry 实现(阻塞)

RedisLockRegistryspring-integration-redis 中提供的 Redis 分布式锁实现类。

集成 spring-integration-redis:

<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-interation</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-redis</artifactId>
</dependency>

注册RedisLockRegistry:

@Configuration
public class RedisLockConfig {
    
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactor) {
        // 第一个参数 redisConnectionFactory
        // 第二个参数 registryKey,分布式锁前缀,建议设置项目名称
        // 该构造方法对应的分布式锁,默认有效期是60秒,可以自定义。
        return new RedisLockRegistry(redisConnectionFactory, "boot-launch");
        // return new RedisLockRegistry(redisConnectionFactory, "boot-launch", 60);
    }
}

使用RedisLockRegistry:

代码实现:

@Resource
private RedisLockRegisty redisLockRegistry;

public void updateUser(String userId) {
    String lockKey = "config" + userId;
    Lock lock = redisLockRegistry.obtain(lockKey); // 获取锁资源
    try {
        lock.lock(); // 加锁
        
        // TODO: 业务代码
    } finally {
        lock.unlock(); // 释放锁
    }
}

注解实现:

@RedisLock("lock-key")
public void save() {
    
}

RedisLockRegistry 实现的分布式锁是支持阻塞的。RedisLocckRegistry 是 Spring Integration Redis 模块中的一个类,它是基于 Redis 实现的分布式锁机制。

当一个线程尝试获取分布式锁时,如果该锁已经被其他线程占用,则当前线程会被阻塞,等待锁释放。阻塞的线程会一直等待,直到锁被成功获取或者等待超时。

阻塞的实现是通过 Redis 的特性实现的:通过 Redis 的 SETNX 命令(SET if Not eXists)尝试将锁的键值对设置到 Redis 中。如果设置成功,则表示获取锁成功;如果设置失败,则表示锁已被其他线程占用,当前线程会被阻塞。

因此,使用 RedisLockRegistry 可以实现支持阻塞的分布式锁机制,能够实现多线程之间的协调和互斥。


3.Redisson 实现(阻塞)

Redission 是一个独立的 Redis 客户端,是与 Jedis、Lettuce 同级别的存在。

集成 Redisson:

<dependency>
	<groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.15.0</version>
    <exclusions>
    	<exclusion>
        	<groupId>org.redisson</groupId>
            <!-- 默认是 Spring Data Redis v.2.3.x 所以排除掉 -->
            <artifactId>redisson-spring-data-23</artifactId>
        </exclusion>
    </exclusions>
</dependency>

配置:

1)在配置文件中添加如下内容:

spring:
  redis:
    redisson:
      file: classpath:redisson.yaml

2)然后新建一个 redisson.yaml 文件,也放在 resources 目录下:

singleServerConfig:
  idleConnectionTimeout: 10000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  password: 123456
  subscriptionsPerConnection: 5
  clientName: null
  address: "redis://192.168.161.3:6379"
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 32
  connectionPoolSize: 64
  database: 0
  dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: "NIO"

代码实现:

@Resource
private RedissonClient redissonClient;

public void updateUser(String userId) {
    String lockKey = "config" + userId;
    RLock lock = redissonClient.getLock(lockKey); // 获取锁资源
    try {
        lock.lock(10, TimeUnit.SECONDS); // 加锁,可以指定锁定时间
        
        // TODO: 业务代码
    } finally {
        lock.unlock(); // 释放锁
    }
}

对比 RedisLockRegistry 和 Redisson 实现:

  • Redisson 优点:可以为每一个锁指定不同的超时时间,而 RedisLockRegistry 目前只能针对所有的锁设置统一的超时时间

注意:如果业务执行超时之后,再去 unlock 会抛出 java.lang.IllegalMonitorStateException

三、思考

1.为什么可以用 setIfAbsent(),而不能用 setnx() ?

  • setIfAbsent() 是 Redis 专门为分布式锁实现的原子操作,其中封装了获取 key、设置 key、设置过期时间等操作。
  • setnx() 主要用于分布式 ID 的生成,如果要用来实现分布式锁的话,虽然可以通过返回结果为0表示获取锁失败,1表示获取锁成功,但是对于设置过期时间的操作需要手动实现,无法保证原子性

整理完毕,完结撒花~ 🌻





参考地址:

1.Java三种方式实现redis分布式锁,https://blog.csdn.net/w_monster/article/details/124472493文章来源地址https://www.toymoban.com/news/detail-657752.html

到了这里,关于Redis学习(八)Java三种方式实现分布式锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JAVA微服务分布式事务的几种实现方式

    一致性(Consistency) :在分布式系统中所有的数据备份,在同一时刻都保持一致状态,如无法保证状态一致,直接返回错误; 可用性(Availability):在集群中一部分节点故障,也能保证客户端访问系统并得到正确响应,允许一定时间内数据状态不一致; 分区容错性(Partiti

    2024年02月12日
    浏览(51)
  • hive-3.1.2分布式搭建与hive的三种交互方式

    在官网或者镜像站下载驱动包 华为云镜像站地址: hive: Index of apache-local/hive/hive-3.1.2 mysql驱动包: Index of mysql-local/Downloads/Connector-J # 1、解压 tar -zxvf apache-hive-3.1.2-bin.tar.gz -C /usr/local/soft/ # 2、重名名 mv apache-hive-3.1.2-bin hive-3.1.2 # 3、配置环境变量 vim /etc/profile # 4、在最后增加

    2024年04月16日
    浏览(43)
  • pytorch 进行分布式调试debug torch.distributed.launch 三种方式

    一. pytorch 分布式调试debug torch.distributed.launch 三种方式 1. 方式1:ipdb调试(建议) 参考之前的博客:python调试器 ipdb 注意:pytorch 分布式调试只能使用侵入式调试,也即是在你需要打断点的地方(或者在主程序的第一行)添加下面的代码: 当进入pdb调试后,跟原先使用pdb调试

    2024年02月07日
    浏览(33)
  • Java中利用Redis,ZooKeeper,数据库等实现分布式锁(遥遥领先)

    1.1 什么是分布式锁 在我们进行单机应用开发涉及并发同步的时候,我们往往采用synchronized或者ReentrantLock的方式来解决多线程间的代码同步问题。但是当我们的应用是在分布式集群工作的情况下,那么就需要一种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题

    2024年02月03日
    浏览(51)
  • Redis——》Redis的部署方式对分布式锁的影响

    推荐链接:     总结——》【Java】     总结——》【Mysql】     总结——》【Redis】     总结——》【Kafka】     总结——》【Spring】     总结——》【SpringBoot】     总结——》【MyBatis、MyBatis-Plus】     总结——》【Linux】     总结——》【MongoDB】    

    2024年02月10日
    浏览(52)
  • 分布式锁设计选型 不可重入锁建议使用ZooKeeper来实现 可重入锁建议使用Redis来实现 分布式锁:ZooKeeper不可重入锁 Java优化建议

    在设计分布式锁时,需要考虑业务场景和业务需求,以保证锁的正确性和可用性。 例如,在一个电商系统中,每个商品都有一个库存量。为了避免多个用户同时购买同一件商品导致库存出现不一致的情况,可以为每个商品设置一个分布式锁,确保同一时间只能有一个用户购买

    2024年02月08日
    浏览(49)
  • 分布式学习第二天 redis学习

    目录 1. 数据库类型 1.1 基本概念 1.2 关系/非关系型数据库搭配使用 2. Redis   2.1 基本知识点 2.2 redis常用命令 2.4 redis数据持久化 3 hiredis的使用 4. 复习 1. 数据库类型 1.1 基本概念 关系型数据库 - sql 操作数据必须要使用sql语句 数据存储在磁盘 存储的数据量大 举例: mysql oracle s

    2024年02月09日
    浏览(37)
  • Redis学习路线(6)—— Redis的分布式锁

    一、分布式锁的模型 (一)悲观锁: 认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。例如Synchronized、Lock都属于悲观锁。 优点: 简单粗暴 缺点: 性能略低 (二)乐观锁: 认为线程安全问题不一定会发生,因此不加锁,只有在更新数据时

    2024年02月14日
    浏览(37)
  • 分布式锁实现(mysql,以及redis)以及分布式的概念

    我旁边的一位老哥跟我说,你知道分布式是是用来干什么的嘛?一句话给我干懵了,我能隐含知道,大概是用来做分压处理的,并增加系统稳定性的。但是具体如何,我却道不出个1,2,3。现在就将这些做一个详细的总结。至少以后碰到面试官可以说上个123。 那么就正式进入

    2024年01月21日
    浏览(62)
  • Redis——》实现分布式锁

    推荐链接:     总结——》【Java】     总结——》【Mysql】     总结——》【Redis】     总结——》【Kafka】     总结——》【Spring】     总结——》【SpringBoot】     总结——》【MyBatis、MyBatis-Plus】     总结——》【Linux】     总结——》【MongoDB】    

    2024年02月10日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包