当前位置: 首页 > article >正文

Redisson和可重入锁初认

文章目录

  • Redisson 简介
    • Redisson 的主要特点
    • Redisson 的核心模块
    • Redisson的优势
    • 使用示例
      • Maven 引入依赖
      • 配置 Redisson 客户端
      • 分布式锁使用示例
    • Redisson 使用场景
      • 总结
  • 可重入锁
    • 关键特性
    • 为什么需要可重入锁
    • 可重入锁的实现方式
    • 可重入锁的实现原理
    • 可重入锁的优缺点
      • 优点
      • 缺点
    • 可重入锁的使用场景
    • 总结

Redisson 简介

  • Redisson 是一个基于 Redis 的 Java 开源框架,旨在为开发者提供简单易用、高效可靠的分布式工具集。它不仅支持常见的 Redis 功能,还封装了大量的分布式结构(如分布式锁、分布式集合、队列等)和高级功能,使得开发者可以在分布式环境中更方便地使用Redis 的能力。
  • Redisson 以 Redis 作为数据存储和通信基础,为分布式系统提供了类似 Java 本地工具的 API,隐藏了底层复杂性,并通过高可靠的机制来确保分布式操作的正确性。

Redisson 的主要特点

  1. 支持多种 Redis 模式:单节点模式、主从模式、哨兵模式、集群模式、云托管服务模式(如 AWS Elasticache、Azure Redis 等)
  2. 分布式工具和数据结构
    • 分布式锁:公平锁、读写锁、可重入锁等。
    • 分布式集合RListRSetRMap 等分布式集合类,封装了 Redis 的数据结构。
    • 分布式队列:支持优先级队列、阻塞队列、延迟队列等。
    • 分布式缓存:支持本地缓存和二级缓存。
  3. 丰富的功能支持
    • 分布式对象:如原子变量(RAtomicLong)、计数器(RCountDownLatch)等。
    • 分布式任务调度:类似 Java ScheduledExecutorService 的分布式任务功能。
    • 事务:提供基于 Redis Lua 脚本的事务支持。
    • 高性能:提供异步和反应式(Reactive)API。
  4. 线程安全:Redisson 所有的分布式工具和数据结构均是线程安全的。
  5. 开发简洁:Redisson 提供了类似 Java 原生 API 的封装,开发起来更加直观和简洁。

Redisson 的核心模块

  1. 分布式锁
    • 可重入锁 (RLock):支持分布式环境下的可重入锁。
    • 公平锁 (RFairLock):保证锁的获取顺序与请求顺序一致。
    • 读写锁 (RReadWriteLock):提供分布式读写锁机制。
    • 信号量 (RSemaphore):类似 Java 的信号量,用于限制并发量。
    • 闭锁 (RCountDownLatch):用于实现分布式闭锁,协调多个线程的启动。
  2. 分布式集合
    • RList:分布式列表,类似 Java 的 List
    • RMap:分布式哈希表,类似 Java 的 HashMap
    • RSet:分布式集合,类似 Java 的 Set
    • RQueue:分布式队列,支持阻塞队列、优先级队列等。
  3. 分布式缓存:支持两级缓存(本地缓存 + Redis 缓存);提供缓存过期和自动清理功能。支持 Spring Cache 接口集成。
  4. 消息队列:支持发布/订阅模式(RTopic)。延迟队列、优先级队列和阻塞队列支持。
  5. 任务调度:提供分布式任务调度功能,类似 Java 的 ScheduledExecutorService。任务调度结果会持久化到 Redis,支持断点续跑。
  6. 异步编程支持:提供异步(CompletableFuture)和反应式(Reactive)API,适合高性能和高并发场景。

Redisson的优势

  1. 封装强大:简化了 Redis 的操作,无需手动处理复杂的 Redis 细节。
  2. 易用性高:API 接口类似于 Java 本地工具,开发成本低。
  3. 高可靠性:实现了分布式锁的可靠机制,防止死锁等问题。
  4. 多功能性:集成了锁、缓存、队列、集合、消息队列等多种功能。
  5. 良好的扩展性:支持各种 Redis 部署模式,适用于单机、集群和云托管环境。

使用示例

Maven 引入依赖

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

配置 Redisson 客户端

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonConfig {
    public static RedissonClient createRedissonClient() {
        // 配置 Redis 连接
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://127.0.0.1:6379") // Redis 地址
              .setPassword(null)                    // Redis 密码(如果有)
              .setDatabase(0);                      // Redis 数据库编号

        // 创建 Redisson 客户端
        return Redisson.create(config);
    }
}

分布式锁使用示例

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;

public class RedissonLockExample {
    public static void main(String[] args) {
        // 创建 Redisson 客户端
        RedissonClient redissonClient = RedissonConfig.createRedissonClient();

        // 获取分布式锁
        RLock lock = redissonClient.getLock("myLock");

        try {
            // 尝试加锁,最多等待 10 秒,锁自动过期时间为 30 秒
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                try {
                    // 加锁成功,执行业务逻辑
                    System.out.println("获取锁成功,执行任务...");
                    Thread.sleep(20000); // 模拟任务耗时 20 秒
                } finally {
                    // 释放锁
                    lock.unlock();
                    System.out.println("任务完成,释放锁!");
                }
            } else {
                System.out.println("获取锁失败!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 关闭 Redisson 客户端
            redissonClient.shutdown();
        }
    }
}

输出示例

  • 如果获取锁成功:
    获取锁成功,执行任务...
    任务完成,释放锁!
    
  • 如果锁被其他客户端占用:
    获取锁失败!
    

Redisson 使用场景

  1. 分布式锁:解决多个进程或线程对同一资源的竞争问题。
  2. 分布式队列:用于任务队列、异步任务处理。
  3. 分布式缓存:构建高效的缓存系统,降低数据库压力。
  4. 分布式任务调度:用于定时任务、延迟任务的分布式执行。
  5. 发布/订阅:用于消息推送和事件通知。

总结

  • Redisson 是一个功能全面、易于使用的分布式工具框架,极大地简化了在 Java 应用中使用 Redis 的复杂度。通过 Redisson,可以高效实现分布式锁、缓存、队列等功能,并且无需直接操作底层 Redis 命令,非常适合需要快速开发分布式系统的场景。

可重入锁

  • 可重入锁(Reentrant Lock) 是一种锁机制,允许同一个线程在持有锁的情况下再次获取同一个锁而不会发生死锁。它记录了锁的获取次数,并且只有当线程释放锁的次数与获取锁的次数相等时,锁才会真正被释放。

关键特性

  1. 同一线程可多次获取
    • 如果一个线程已经持有锁,它可以在未释放锁的情况下再次获取锁。
    • 每次获取锁时,锁的计数器会加1。
    • 每次释放锁时,计数器会减1,只有计数器归零时,锁才会真正释放。
  2. 避免死锁
    • 如果没有可重入特性,一个线程在持有锁的情况下再次尝试获取锁会导致自己被锁住(死锁)。
    • 可重入锁通过记录锁的持有线程及其获取次数避免了这种情况。

为什么需要可重入锁

在实际编程中,很多场景中需要调用嵌套的同步代码,例如:

public synchronized void methodA() {
    System.out.println("Method A");
    methodB(); // 同一个线程再进入另一个同步方法
}

public synchronized void methodB() {
    System.out.println("Method B");
}
  • methodAmethodB 都是同步方法,意味着它们会尝试获取同一个对象锁
  • 如果锁不是可重入的,线程进入 methodA 后,试图在未释放锁的情况下调用 methodB 时会发生死锁。
  • 可重入锁的机制允许线程多次进入同步代码块,从而避免死锁问题。

可重入锁的实现方式

  1. 内置锁(synchronized)
    • Java 的 synchronized 是一种可重入锁。
    • 当一个线程进入 synchronized 方法或代码块后,它可以进入该对象中其他标记为 synchronized 的方法或代码块,而无需再次获取锁。
  • 示例代码:

    public class ReentrantLockExample {
        public synchronized void methodA() {
            System.out.println(Thread.currentThread().getName() + "进入methodA");
            methodB();
        }
    
        public synchronized void methodB() {
            System.out.println(Thread.currentThread().getName() + "进入methodB");
        }
    
        public static void main(String[] args) {
            ReentrantLockExample example = new ReentrantLockExample();
            new Thread(example::methodA, "线程1").start();
        }
    }
    
  • 运行结果:

    线程1进入methodA
    线程1进入methodB
    
  1. 显式锁(ReentrantLock)
    • Java 提供了java.util.concurrent.locks.ReentrantLock,它是一个更加灵活的可重入锁。与synchronized 相比,ReentrantLock 提供了更多功能,比如:尝试获取锁(tryLock)、可中断获取锁(lockInterruptibly)、支持公平锁模式。
    • 示例代码:
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ReentrantLockExample {
        private final ReentrantLock lock = new ReentrantLock();
        public void methodA() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "进入methodA");
                methodB();
            } finally {
                lock.unlock();
            }
        }
    
        public void methodB() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "进入methodB");
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            ReentrantLockExample example = new ReentrantLockExample();
            new Thread(example::methodA, "线程1").start();
        }
    }
    
    • 运行结果:
    线程1进入methodA
    线程1进入methodB
    

可重入锁的实现原理

  1. 内置锁的实现

    • Java 的内置锁(synchronized)由 JVM 实现,底层通过对象头中的标记字段来记录锁的状态。
    • 如果一个线程已经持有锁,它可以再次进入,并在退出时递减计数器,直到计数器为零时释放锁。
  2. ReentrantLock 的实现

    • ReentrantLock 的底层使用 AbstractQueuedSynchronizer(AQS)实现。
    • 核心逻辑
      • 锁记录持有锁的线程 ID 和重入次数(state)。
      • 每次获取锁时,检查当前线程是否已经持有锁,如果是,则增加重入次数。
      • 每次释放锁时,递减计数器,只有计数器为零时才会真正释放锁。

可重入锁的优缺点

优点

  1. 避免死锁:允许同一线程在锁内反复进入,避免了因嵌套调用产生的死锁问题。
  2. 灵活性:使用 ReentrantLock 可以更灵活地尝试获取锁、设置超时、支持公平模式等。
  3. 线程安全:在多线程环境下确保数据一致性。

缺点

  1. 性能开销:由于需要维护锁的状态(比如计数器),可能稍微增加开销。尤其是在轻量级同步场景下,相比无锁机制稍慢。
  2. 复杂性:显式锁(如 ReentrantLock)需要显式释放锁,容易因代码逻辑问题导致忘记释放锁,从而引发死锁。

可重入锁的使用场景

  1. 嵌套同步代码块:当线程需要多次进入同一个锁时,避免死锁。
  2. 分布式系统中实现分布式锁:例如Redisson 提供的分布式锁就支持可重入功能。
  3. 需要灵活的锁控制:如果需要控制超时时间、可中断锁获取等功能,可以使用显式的 ReentrantLock

总结

可重入锁是一种解决嵌套同步问题的重要机制,广泛应用于多线程编程中。在 Java 中,synchronizedReentrantLock 都是可重入锁。根据实际需求选择使用内置锁或显式锁,可以在保证线程安全的同时兼顾性能和开发效率。


http://www.kler.cn/a/500506.html

相关文章:

  • vue3 初体验
  • HTML5实现好看的中秋节网页源码
  • _STM32关于CPU超频的参考_HAL
  • Level DB --- filter_block
  • screenpipe - 全天候录制屏幕的 AI 助手
  • DDD - 微服务设计与领域驱动设计实战(上)_统一建模语言及事件风暴会议
  • Flink系统知识讲解之:Flink内存管理详解
  • 【STM32-学习笔记-1-】GPIO
  • CSS响应式
  • spring boot 集成 knife4j
  • 2024年有哪些人工智能书籍推荐?
  • Facebook 隐私变革之路:回顾与展望
  • 04:同步与互斥
  • 【教程】Unity 本地化多语种 | Localization 工具组
  • 【PDF转Word】 PDF在线转word文档 好用!优质网站资源推荐
  • Java 实现 Elasticsearch 查询当前索引全部数据
  • OOM排查思路
  • 蓝桥杯历届真题 #食堂(C++,Java)
  • 探讨人工智能机器人学之路径规划与导航:A*算法、Dijkstra算法等路径规划方法
  • 《零基础Go语言算法实战》【题目 2-12】Go 语言接口的工作原理
  • 冒泡排序基础与实现
  • 微服务的配置共享
  • C# OpenCV机器视觉:波形相似度