专业的JAVA编程教程与资源

网站首页 > java教程 正文

分布式锁系列一:基于Redis SETNX命令实现分布式锁

temp10 2024-11-22 20:55:20 java教程 12 ℃ 0 评论

目前大多数单体应用系统中,微服务应用系统中以及分布式系统中,Redis作为缓存中间件被广泛使用。Redis被公认的是基于内存的,高效的,支持多种数据结构的缓存框架,Redis官方网站有这样一段话,对其进行了清晰的描述:

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.

分布式锁系列一:基于Redis SETNX命令实现分布式锁

从Redis的官方文档描述,Redis不仅仅可以用来作为高效的数据缓存;基于Redis的高级特性,我们可以基于Redis实现消息队列,分布式锁等功能。


在单体应用系统中,所有的业务和功能都在一个应用系统中,也相当所有业务和功能都在一个线程中运行,如果需要对某一资源或者某一变量进行加锁操作,我们完全可以基于程序本身使用的语言的锁机制来实现,比如Java我们可以采用synchronized和Lock的实现类。

在分布式和微服务系统中,不同功能或者不同业务被拆分成独立的应用,并且完成独立的功能。在部署方式上独立的应用分别被部署到不同的机器上,以及采用集群化部署,一个独立的应用被部署为多个副本。相比于单体应用最直观的区别是:不同的功能或者不同的业务是跨进程运行。


那么,在分布式应用系统中,我们如何对某一竞争资源进行加锁操作呢?

答案:当然是我们需要一种分布式锁机制。

实现分布式锁有多种解决方案,常用的解决方案整理如下:

  • 基于Redis对字符串操作命令SETNX实现;
  • 基于Zookeeper的临时有序节点实现;

由于Redis是单线程的,基于命令SETNX(set if not exists)可以实现锁的机制;

当一个key在Redis中不存在时;返回1(成功);

当一个key在Redis中存在时;返回0(失败);

127.0.0.1:6379> SETNX abc 1
(integer) 1
127.0.0.1:6379> SETNX abc 1
(integer) 0

在Redis的帮助文档中,对该SETNX命令是这样描述的:

127.0.0.1:6379> help @string

  ........................................

  SETNX key value
  summary: Set the value of a key, only if the key does not exist
  since: 1.0.0

下面我们通过一个简单的demo来演示下,基于Redis如何实现分布式锁机制。

新建一个Java工程redis-distributed-lock,maven依赖如下:

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

redisson封装了对Redis的大多数操作命令,相当于Redis的Java客户端。我们只需要做简单的配置,就可以像redis-cli那样操作Redis。当然,这里也可以选择其他的Redis Java客户端,例如Jedis,RedisTemplate等。

定义一个main函数

public class RedisLockDemo {
    public static void main(String[] args) throws IOException {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        config.useSingleServer().setPassword("123456");
        RedissonClient redissonClient = Redisson.create(config);
        RLock rLock = redissonClient.getLock("lock");
        for (int i = 0; i < 20; i++) {
            //开启自线程
            new Thread(() -> {
                //加锁
                rLock.lock();
                System.out.println(Thread.currentThread().getName() + " 获取锁");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放锁
                    rLock.unlock();
                }
            }).start();
        }
    }
}

在这里我们采用多线程来模拟一个分布式系统,每一个线程代表一个独立的应用系统。我们在同一时间开启20个线程,这20个线程同时去抢占锁,如果某一线程抢占到锁,则打印“获取到锁”,没有抢占到锁的线程则处于等待状态。当某一线程抢占到锁后,完成操作后释放掉锁,则其他未抢占到锁的线程被唤醒,并且同时再一次去抢占锁。

运行mian函数执行结果如下:

从代码层面和运行结果层面,我们可以看到基于Redis实现了分布式锁的机制。redisson这个Java版的Redis客户端封装了对锁的实现细节,具体封装了如下:

  • 所有线程同时抢占锁,以及抢占到锁的线程释放锁;
  • 未抢占到锁的线程阻塞等待;
  • 抢占到锁的线程释放锁后,唤醒未抢占到锁的线程,并且同时再次抢占锁;


参考:

https://redis.io/

demo源码:

https://github.com/bq-xiao/distributed-lock-demo.git

不积跬步,无以至千里;不积小流,无以成江海!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表