基于Redis的分布式锁

作者: 沐风之境

基于Redis的分布式锁

Redis使用redisson来实现分布式锁

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

Redisson文档

https://github.com/redisson/redisson/wiki/1.-概述

redisson配置

@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://localhost:6379")
                .setDatabase(0);
        return Redisson.create(config);
    }
}

常用的分布式锁的种类

可重入锁(ReentrantLock)

​ 对于同一把锁,同一个申请者或者线程在已经获取到锁的情况下,可以重复申请锁,而不会导致死锁情况发生的一种锁

@Component
public class LockTest {
    private static final int THREAD_NUMBER = 10;

    @Autowired
    private RedissonClient redissonClient;

    // 可重入锁
    public void reentrantLock() throws InterruptedException{
        final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUMBER);
        final ExecutorService executorService = Executors.newCachedThreadPool();

        System.out.println("测试可重入锁");
        System.out.println("============================================================");
        for (int i = 1; i <= THREAD_NUMBER; i++) {
            int finalI = i;
            executorService.submit(() -> {
                final RLock lock = redissonClient.getLock("reentrantLock");
                try {
                    if (lock.tryLock(100, 4, TimeUnit.SECONDS)) {
                        System.out.println("获得锁 Thread- " + finalI);
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    lock.unlock();
                    System.out.println("释放锁 Thread- " + finalI);
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        System.out.println("结束...");
        System.out.println();
    }
}
/* 输出(两两输出)
测试可重入锁
============================================================
获得锁 Thread- 2
释放锁 Thread- 2
获得锁 Thread- 1
释放锁 Thread- 1
获得锁 Thread- 7
释放锁 Thread- 7
获得锁 Thread- 6
释放锁 Thread- 6
获得锁 Thread- 10
释放锁 Thread- 10
获得锁 Thread- 4
释放锁 Thread- 4
获得锁 Thread- 8
释放锁 Thread- 8
获得锁 Thread- 9
释放锁 Thread- 9
获得锁 Thread- 5
释放锁 Thread- 5
获得锁 Thread- 3
释放锁 Thread- 3
结束...

*/

可重入锁

公平锁(fairLock)

按申请前后的顺序获取到锁,先去申请的线程先获取到锁

@Component
public class LockTest {
    private static final int THREAD_NUMBER = 10;

    @Autowired
    private RedissonClient redissonClient;

    public void fairLock() throws InterruptedException{
        final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUMBER);
        final ExecutorService executorService = Executors.newCachedThreadPool();

        System.out.println("测试公平锁");
        System.out.println("============================================================");
        for (int i = 1; i <= THREAD_NUMBER; i++) {
            int finalI = i;
            executorService.submit(() -> {
                final RLock lock = redissonClient.getFairLock("fairLock");
                try {
                    if (lock.tryLock(100, 4, TimeUnit.SECONDS)) {
                        System.out.println("获得锁 Thread- " + finalI);
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    lock.unlock();
                    System.out.println("释放锁 Thread- " + finalI);
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        System.out.println("结束...");
        System.out.println();
    }
}

/* 输出(两两输出)
测试公平锁
============================================================
获得锁 Thread- 4
释放锁 Thread- 4
获得锁 Thread- 2
释放锁 Thread- 2
获得锁 Thread- 8
释放锁 Thread- 8
获得锁 Thread- 1
释放锁 Thread- 1
获得锁 Thread- 7
释放锁 Thread- 7
获得锁 Thread- 10
释放锁 Thread- 10
获得锁 Thread- 6
释放锁 Thread- 6
获得锁 Thread- 3
释放锁 Thread- 3
获得锁 Thread- 9
释放锁 Thread- 9
获得锁 Thread- 5
释放锁 Thread- 5
结束...

*/

公平锁

读写锁(RedWriteLock)

​ 针对读操作不会引起资源争抢的情况,做的优化锁。它允许同时拥有多把读书和只有一把写锁

@Component
public class LockTest {
    private static final int THREAD_NUMBER = 10;

    @Autowired
    private RedissonClient redissonClient;

    public void readWriteLock() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUMBER);
        final ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("读写锁");
        System.out.println("============================================================");
        final RReadWriteLock lock = redissonClient.getReadWriteLock("readWriteLock");

        AtomicInteger radData = new AtomicInteger();

        boolean isRead = true;

        for (int i = 1; i <= THREAD_NUMBER; i++) {
            int finalI = i;
            if (isRead) {
                executorService.submit(() -> {
                        try {
                            if (lock.readLock().tryLock(100, 30, TimeUnit.SECONDS)) {
                                System.out.println("获得Read锁 Thread-" + finalI);
                                Thread.sleep(1000);
                                System.out.println("读取到数据: " + radData.getOpaque());
                                System.out.println("没有获取到读锁 Thread-" + finalI);
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            lock.readLock().unlock();
                            countDownLatch.countDown();
                        }
                });
                executorService.submit(() -> {
                    try {
                        if (lock.writeLock().tryLock(100, 30, TimeUnit.SECONDS)) {
                            System.out.println("获得Writer锁 Thread-" + finalI);
                            Thread.sleep(1000);
                            final int value = radData.addAndGet(1);
                            System.out.println("写成功:" + value);
                            System.out.println("没有获取到写锁 Thread-" + finalI);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        lock.writeLock().unlock();
                        System.out.println("释放锁 Thread- " + finalI);
                        countDownLatch.countDown();
                    }
                });
            }
            isRead = !isRead;
        }
        countDownLatch.await();
        System.out.println("结束...");
        System.out.println();
    }
}

/* 输出
读写锁
============================================================
获得Writer锁 Thread-10
写成功:1
释放锁 Thread- 10
获得Writer锁 Thread-6
写成功:2
释放锁 Thread- 6
获得Read锁 Thread-3
获得Read锁 Thread-5
获得Read锁 Thread-7
获得Read锁 Thread-1
获得Read锁 Thread-9
读取到数据: 2
读取到数据: 2
读取到数据: 2
读取到数据: 2
读取到数据: 2
获得Writer锁 Thread-4
写成功:3
释放锁 Thread- 4
获得Writer锁 Thread-2
写成功:4
释放锁 Thread- 2
获得Writer锁 Thread-8
写成功:5
释放锁 Thread- 8
结束...
*/

读写锁

信号量(Semaphore)

​ 对于同一个资源的并发度做的锁限制,在实例化的时候可以指定许可的数量。每个并发线程要持有一个许可,当许可都被领取空时,后面的线程就会阻塞。常用于做并发度的流控

@Component
public class LockTest {
    private static final int THREAD_NUMBER = 10;

    @Autowired
    private RedissonClient redissonClient;

    public void semaphoreLock() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUMBER);
        final ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("测试信号量锁");
        System.out.println("============================================================");
        final RSemaphore semaphore = redissonClient.getSemaphore("semaphoreLock");
        semaphore.trySetPermits(2);
        for (int i = 1; i <= THREAD_NUMBER; i++) {
            int finalI = i;
            executorService.submit(() -> {
                try {
                    semaphore.acquire();
                    System.out.println("取得锁 Thread-" + finalI);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    semaphore.release();
                    System.out.println("释放锁 Thread- " + finalI);
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        System.out.println("结束...");
        System.out.println();
    }
}
/* 输出

只有两个并发度,两个线程同时执行

测试信号量锁
============================================================
取得锁 Thread-3
取得锁 Thread-1
释放锁 Thread- 1
释放锁 Thread- 3
取得锁 Thread-7
取得锁 Thread-4
释放锁 Thread- 4
释放锁 Thread- 7
取得锁 Thread-10
取得锁 Thread-2
释放锁 Thread- 2
释放锁 Thread- 10
取得锁 Thread-6
取得锁 Thread-5
释放锁 Thread- 5
释放锁 Thread- 6
取得锁 Thread-8
取得锁 Thread-9
释放锁 Thread- 8
释放锁 Thread- 9
结束...

*/

信号量锁

倒计锁(CountDownLatch)

​ 常用于协调多个线程,互相等待的场景。在全部线程执行完成后,结束阻塞

@Component
public class LockTest {
    private static final int THREAD_NUMBER = 10;

    @Autowired
    private RedissonClient redissonClient;

    public void countDownLatchLock() throws InterruptedException {
//        final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUMBER);
        final ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("倒数同步锁");
        System.out.println("============================================================");
        final RCountDownLatch countDownLatch = redissonClient.getCountDownLatch("countDownLatchLock");
        countDownLatch.trySetCount(THREAD_NUMBER);
        for (int i = 1; i <= THREAD_NUMBER; i++) {
            int finalI = i;
            executorService.submit(() -> {
                try {
                    System.out.println("取得锁 Thread-" + finalI);
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    countDownLatch.countDown();
                    System.out.println("释放锁 Thread- " + finalI);
                }
            });
        }
        countDownLatch.await();
        System.out.println("结束...");
        System.out.println();
    }
}

/* 输出
倒数同步锁
============================================================
取得锁 Thread-1
取得锁 Thread-7
取得锁 Thread-8
取得锁 Thread-3
取得锁 Thread-6
取得锁 Thread-4
取得锁 Thread-5
取得锁 Thread-10
取得锁 Thread-9
取得锁 Thread-2
释放锁 Thread- 4
释放锁 Thread- 9
释放锁 Thread- 1
释放锁 Thread- 8
释放锁 Thread- 3
释放锁 Thread- 2
释放锁 Thread- 6
释放锁 Thread- 5
释放锁 Thread- 7
释放锁 Thread- 10
结束...
*/

倒数同步锁

原文创作:沐风之境

原文链接:https://www.cnblogs.com/mufeng3421/p/13619369.html

更多推荐

更多
  • Java8反应式编程指南-六、使用调度器获得并发性和并行性 RxJava 的调度器,缓冲、节流和去抖动,调试可观察对象及其调度器,可观察的间隔及其默认调度程序,调度器的类型,将可观察对象和调度器相结合,平行性,节流,去抖动,缓冲器和窗口操作器,背压操作人员,调度器。即时调度器,调度员。蹦床调度员
  • Java8反应式编程指南-七、测试 RxJava 应用 使用简单订阅进行测试,阻塞可观测类,聚合运算符和 BlockingObservable 类,使用聚合运算符和 BlockingObservable 类进行测试,使用 TestSubscriber 类进行深入测试,借助 TestSched
  • Java8反应式编程指南-八、资源管理与 RxJava 扩展 资源管理,使用 Observable.cache 缓存数据,使用升降机创建自定义操作员,使用 Observable.compose 运算符组合多个运算符,介绍可观察的使用方法, 通过前面的章节,我们已经学习了如何使用 RxJava
  • Java8反应式编程指南-五、组合器、条件和错误处理 结合可观察实例,条件运算符,处理错误,HTTP 客户端示例,拉链操作员,组合测试操作符,合并运算符,concat 操作员,电磁轴承操作员,takeUntil、takeWhile、skipUntil和 skipWhile条件运算符,def
  • Java8反应式编程指南-四、转换、过滤和积累您的数据 可观测变换,过滤数据,积累数据,使用各种 flatMap 运算符的变换,分组项目,附加有用的变换运算符, 现在我们有了从各种源数据创建`Observable`实例的方法,是时候围绕这些实例构建编程逻辑了。我们将介绍用于实现逐步计算
  • Java8反应式编程指南-三、创建和连接可观察对象、观察者和主体 从方法中观察到的,可观察的、公正的方法,其他可观察的工厂方法,可观察的创建方法,订阅和取消订阅,冷热可观察实例,主体实例,可连通可观测类, RxJava 的`Observable`实例是反应式应用的构建块,RxJava 的这一优势
  • Java8反应式编程指南-一、反应式编程简介 什么是反应式编程?,我们为什么要被动?,介绍 RxJava,下载并设置 RxJava,比较迭代器模式和 RxJava 可观测值, 如今,术语反应式编程正在成为一种趋势。各种编程语言的库和框架正在涌现。关于反应式编程的博客文
  • Java8反应式编程指南-二、使用 Java 8 的函数结构 Java 8 中的 Lambdas,用 lambdas 实现无功和示例,纯函数与高阶函数,引入新的语法和语义,Java 8 和 RxJava 中的功能接口,纯函数,高阶函数,RxJava 与函数式编程, 函数式编程不是一个新概念;
  • Java8反应式编程指南-零、序言 这本书涵盖的内容,这本书你需要什么,这本书是给谁的,公约,读者反馈,客户支持,下载示例代码,勘误表,盗版,问题, 反应式编程已经存在了几十年。从 Smalltalk 还是一种年轻的语言时起,就有一些反应式编程的实现。然而,它只是最
  • Go编程秘籍-三、数据转换与组合 本章将展示一些在数据类型之间转换、使用非常大的数字、使用货币、使用不同类型的编码和解码(包括 Base64 和gob)以及使用闭包创建自定义集合的示例。转换数据类型和接口转换,使用 math 和 math/big ...
  • 近期文章

    更多
    文章目录

      推荐作者

      更多