《伸手系列》之分布式锁Redssion入门和源码解析

Redisson简介

Javaer都知道Jedis,Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。Redission也是Redis的客户端,相比于Jedis功能简单。Jedis简单使用阻塞的I/O和redis交互,Redission通过Netty支持非阻塞I/O。Jedis最新版本2.9.0是2016年的快3年了没有更新,而Redission最新版本是2018.10月更新。
Redission封装了锁的实现,其继承了java.util.concurrent.locks.Lock的接口,让我们像操作我们的本地Lock一样去操作Redission的Lock。
下面直接上干货

使用样例

@GetMapping("/testLock")
	public String lock(){
	    
		RLock lock = redissonClient.getLock("anyLock");

		lock.lock();

		try {
				System.out.println(lock);
				Thread.sleep(TimeUnit.SECONDS.toMillis(30));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
		    lock.unlock();
		}
		
		return "ok" ;
	}

源码分析

  • 获取锁
    《伸手系列》之分布式锁Redssion入门和源码解析_第1张图片

调用getLock()方法后实际返回一个RedissonLock对象

  • 加锁
    《伸手系列》之分布式锁Redssion入门和源码解析_第2张图片《伸手系列》之分布式锁Redssion入门和源码解析_第3张图片

在RedissonLock对象的lock()方法主要调用tryAcquire()方法,由于leaseTime == -1,于是走tryLockInnerAsync()方法

  • 加锁细节
    《伸手系列》之分布式锁Redssion入门和源码解析_第4张图片

结合上面的参数声明,我们可以知道,这里KEYS[1]就是getName(),ARGV[2]是getLockName(threadId),假设前面获取锁时传的name是“anyLock”,假设调用的线程ID是Thread-1,假设成员变量UUID类型的id是85b196ce-e6f2-42ff-b3d7-6615b6748b5d:65那么KEYS[1]=anyLock,ARGV[2]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1 ,因此,这段脚本的意思是1、判断有没有一个叫“anyLock”的key2、如果没有,则在其下设置一个字段为“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”,值为“1”的键值对 ,并设置它的过期时间3、如果存在,则进一步判断“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”是否存在,若存在,则其值加1,并重新设置过期时间4、返回“anyLock”的生存时间(毫秒)

  • 加锁redis结构
    《伸手系列》之分布式锁Redssion入门和源码解析_第5张图片

这里用的数据结构是hash,hash的结构是: key 字段1 值1 字段2 值2 。。。用在锁这个场景下,key就表示锁的名称,也可以理解为临界资源,字段就表示当前获得锁的线程所有竞争这把锁的线程都要判断在这个key下有没有自己线程的字段,如果没有则不能获得锁,如果有,则相当于重入,字段值加1(次数)

  • 解锁
    《伸手系列》之分布式锁Redssion入门和源码解析_第6张图片

我们还是假设name=anyLock,假设线程ID是Thread-1,同理,我们可以知道KEYS[1]是getName(),即KEYS[1]=anyLock,KEYS[2]是getChannelName(),即KEYS[2]=redisson_lock__channel:{anyLock},ARGV[1]是LockPubSub.unlockMessage,即ARGV[1]=0,ARGV[2]是生存时间,ARGV[3]是getLockName(threadId),即ARGV[3]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1,因此,上面脚本的意思是:1、判断是否存在一个叫“anyLock”的key2、如果不存在,向Channel中广播一条消息,广播的内容是0,并返回1。3、如果存在,进一步判断字段85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1是否存在。4、若字段不存在,返回空,若字段存在,则字段值减1,5、若减完以后,字段值仍大于0,则返回0。6、减完后,若字段值小于或等于0,则广播一条消息,广播内容是0,并返回1;可以猜测,广播0表示资源可用,即通知那些等待获取锁的线程现在可以获得锁了

  • 等待
 private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
        long threadId = Thread.currentThread().getId();
        Long ttl = tryAcquire(leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return;
        }

        RFuture<RedissonLockEntry> future = subscribe(threadId);
        if (interruptibly) {
            commandExecutor.syncSubscriptionInterrupted(future);
        } else {
            commandExecutor.syncSubscription(future);
        }

        try {
            while (true) {
                ttl = tryAcquire(leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) {
                    break;
                }

                // waiting for message
                if (ttl >= 0) {
                    try {
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        if (interruptibly) {
                            throw e;
                        }
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    }
                } else {
                    if (interruptibly) {
                        future.getNow().getLatch().acquire();
                    } else {
                        future.getNow().getLatch().acquireUninterruptibly();
                    }
                }
            }
        } finally {
            unsubscribe(future, threadId);
        }
//        get(lockAsync(leaseTime, unit));
    }

这里会订阅Channel,当资源可用时可以及时知道,并抢占,防止无效的轮询而浪费资源当资源可用用的时候,循环去尝试获取锁,由于多个线程同时去竞争资源,所以这里用了信号量,对于同一个资源只允许一个线程获得锁,其它的线程阻塞

  • 总结
    《伸手系列》之分布式锁Redssion入门和源码解析_第7张图片
    《伸手系列》之分布式锁Redssion入门和源码解析_第8张图片

关注Github:1/2极客

关注博客:御前提笔小书童

关注网站:HuMingfeng

关注公众号:开发者的花花世界

你可能感兴趣的:(Java的小小世界,Redis的私人地带)