JedisLock基于Redis实现分布式锁

一、 背景
实际的项目开发过程中我们常常遇到高并发访问,容易导致数据不同步,例如:库存的增加, 故此我们可以通过Redis提供的特性实现分布式锁,以达到数据的同步性。本文通过Redis的客户端JedisLock来实现。

注:该文是本博主记录学习之用,没有太多详细的讲解,敬请谅解!

二、 Redis实现分布式锁的原理
1、选用Redis实现分布式锁原因:

(1)Redis有很高的性能;
(2)Redis命令对此支持较好,实现起来比较方便

2、使用命令介绍:

(1)SETNX

SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

(2)expire

expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

(3)delete

delete key:删除key

在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

3、实现思想:

(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

流程图:

JedisLock基于Redis实现分布式锁_第1张图片

 

三、添加Maven依赖

    com.github.jedis-lock
    jedis-lock
    1.0.0


四、代码示例

package com.vcyber.user.util;

import com.github.jedis.lock.JedisLock;
import redis.clients.jedis.Jedis;

public class JedisLockTest {

    public static void main(String[] args) throws InterruptedException {
        for (int i=0;i<10;i++){
            int num = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Jedis jedis=new Jedis("127.0.0.1",6379);
                    lock(jedis,"key", num);
                }
            }).start();
        }

    }

    public static void lock(Jedis jedis, String key,int i){
        JedisLock jedisLock=new JedisLock(jedis,key,2000,2000);
        try {
            //获得锁
            jedisLock.acquire();
            System.out.println(i+"获得锁");
            if(i==2){
                //该代码块是为了验证效果
                System.out.println(i+"睡眠2秒....");
                Thread.sleep(2000);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println(i+"释放锁");
            if(jedisLock!=null){
                //释放锁
                jedisLock.release();
            }
            if(jedis!=null){
                jedis.close();
            }
        }
    }

}


效果图:

JedisLock基于Redis实现分布式锁_第2张图片

JedisLock基于Redis实现分布式锁_第3张图片

 

对于JedisLock如何基于Redis来实现锁的效果,本文就不一一描述,各位可自行看源码理解!

以下贴上源码以供学习:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.github.jedis.lock;

import redis.clients.jedis.Jedis;

public class JedisLock {
    Jedis jedis;
    String lockKey;
    int expireMsecs;
    int timeoutMsecs;
    boolean locked;

    public JedisLock(Jedis jedis, String lockKey) {
        this.expireMsecs = 60000;
        this.timeoutMsecs = 10000;
        this.locked = false;
        this.jedis = jedis;
        this.lockKey = lockKey;
    }

    public JedisLock(Jedis jedis, String lockKey, int timeoutMsecs) {
        this(jedis, lockKey);
        this.timeoutMsecs = timeoutMsecs;
    }

    public JedisLock(Jedis jedis, String lockKey, int timeoutMsecs, int expireMsecs) {
        this(jedis, lockKey, timeoutMsecs);
        this.expireMsecs = expireMsecs;
    }

    public JedisLock(String lockKey) {
        this((Jedis)null, lockKey);
    }

    public JedisLock(String lockKey, int timeoutMsecs) {
        this((Jedis)null, lockKey, timeoutMsecs);
    }

    public JedisLock(String lockKey, int timeoutMsecs, int expireMsecs) {
        this((Jedis)null, lockKey, timeoutMsecs, expireMsecs);
    }

    public String getLockKey() {
        return this.lockKey;
    }

    public synchronized boolean acquire() throws InterruptedException {
        return this.acquire(this.jedis);
    }
    
    /**
     * 获得 lock.
     * 实现思路: 主要是使用了redis 的setnx命令,缓存了锁.
     * reids缓存的key是锁的key,所有的共享, value是锁的到期时间(注意:这里把过期时间放在value了,没有时间上设置其超时时间)
     * 执行过程:
     * 1.通过setnx尝试设置某个key的值,成功(当前没有这个锁)则返回,成功获得锁
     * 2.锁已经存在则获取锁的到期时间,和当前时间比较,超时的话,则设置新的值
     *
     */
     
    public synchronized boolean acquire(Jedis jedis) throws InterruptedException {
        int timeout = this.timeoutMsecs;

        while(timeout >= 0) {
            long expires = System.currentTimeMillis() + (long)this.expireMsecs + 1L;
            String expiresStr = String.valueOf(expires);  //锁到期时间
            if (jedis.setnx(this.lockKey, expiresStr) == 1L) {
                this.locked = true;
                return true;
            }
          //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
          //获取上一个锁到期时间,并设置现在的锁到期时间,
          //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
            String currentValueStr = jedis.get(this.lockKey);
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                String oldValueStr = jedis.getSet(this.lockKey, expiresStr);
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    this.locked = true;
                    return true;
                }
            }

            timeout -= 100;
            Thread.sleep(100L);
        }

        return false;
    }

    public synchronized void release() {
        this.release(this.jedis);
    }

    public synchronized void release(Jedis jedis) {
        if (this.locked) {
            jedis.del(new String[]{this.lockKey});
            this.locked = false;
        }

    }
}

你可能感兴趣的:(redis锁)