前言上一篇写了Redis分布式锁的原理和缺陷,具体原理我还没说呢。过年后暂时无事可做。反正我是闲的。还是学习Redisson的源码比较好。虽然是心血来潮,但仔细研究后发现,Redisson的源码解释工作量还是挺大的,它使用了大量的Java并发类,使用Netty作为通信工具,实现与Redis组件的远程调用。如果把这些知识点全部讲解完是不太现实的。这篇文章的重点主要是Redisson分布式锁的实现原理,所以网络通信和并发原理的代码解读就不会太细心了。不足之处敬请见谅!在Redis发布和订阅之前说过,分布式锁其实有3个核心功能:加锁、解锁和设置锁超时时间。这三个函数也是我们研究Redisson分布式锁原理的方向。在学习之前,我们需要了解一个知识点,就是Redis的发布订阅功能。Redis发布-订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息,发布者可以向指定的通道(channel)发送消息,如果subscriber订阅了一个channel,就可以接收消息,从而实现多客户端的通讯效果。订阅命令为SUBSCRIBEchannel[channel...],可以订阅一个或多个频道。当通过PUBLISH命令向通道发送新消息时,订阅者可以接收到消息,就像这样:打开两个客户端,一个订阅通道channel1,另一个通过PUBLISH发送消息,订阅的那个可以收到。这种模式可以实现不同客户端之间的通信。这种通信方式的神奇应用场景我们就不展开了。大家可以自己去网上查一下,学习一下。我们的主角依然是Redisson。热身后,就该上主菜了。在使用Redisson锁定Redisson源码前,需要先获取一个RLock实例对象。有了这个对象,就可以调用lock和tryLock方法来完成加锁功能。Configconfig=newConfig();config.useSingleServer().setPassword("").setAddress("redis://127.0.0.1:6379");RedissonClientredisson=Redisson.create(config);//RLock对象RLocklock=redisson.getLock("我的锁");配置对应的host,然后就可以创建RLock对象了。RLock是一个接口,具体的synchronizer需要实现这个接口。当我们调用redisson.getLock()时,程序会初始化一个默认的同步执行器RedissonLock,它会初始化几个参数,commandExecutor:异步Executor执行器,Redisson中的所有命令都是通过...Executor执行的;id:唯一ID,初始化时用UUID创建;internalLockLeaseTime:等待获取锁的时间,这里读取的是配置类中的默认定义,时间为30秒;同时,我在图中也标注了一个方法getEntryName,返回的是一串“ID:锁名”,表示当前持有对应锁的线程的标识。这些参数必须留下印象。它经常出现在源代码分析中。说完初始化,我们就可以开始学习加锁和解锁的源码了。Redisson有两种加锁方式,tryLock和lock。使用上的区别是tryLock可以设置锁的过期时间leaseTime和waitTime。核心处理逻辑类似。让我们从tryLock开始。tryLock代码有点长。..不方便补图,直接贴上去,/***@paramwaitTime等待锁的时间*@paramleaseTime锁持有时间*@paramunit时间单位*@return*@throwsInterruptedException*/publicbooleantryLock(longwaitTime,longleaseTime,TimeUnitunit)throwsInterruptedException{//等待锁的剩余时间longtime=unit.toMillis(waitTime);longcurrent=System.currentTimeMillis();finallongthreadId=Thread.currentThread().getId();//尝试获取锁,如果没有加锁,返回锁的剩余超时时间longttl=tryAcquire(leaseTime,unit,threadId);//ttl为null,表示可以抢到锁,returntrueif(ttl==null){returntrue;}//如果waitTime已经超时,则返回false,表示申请锁失败time-=(System.currentTimeMillis()-current);if(time<=0){acquireFailed(threadId);returnfalse;}current=System.currentTimeMillis();//订阅分布式锁,解锁时通知。看,这里我们使用了我们上面提到的发布订阅。finalRFuturesubscribeFuture=subscribe(threadId);//阻塞等待锁释放,await()返回false,表示等待超时if(!await(subscribeFuture,time,TimeUnit.MILLISECONDS)){if(!subscribeFuture.cancel(false)){subscribeFuture.addListener(newFutureListener(){@OverridepublicvoidoperationComplete(Futurefuture)throwsException{if(subscribeFuture.isSuccess()){//等待超时,取消订阅直接unsubscribe(subscribeFuture,threadId);}}});}acquireFailed(threadId);returnfalse;}try{time-=(System.currentTimeMillis()-current);if(time<=0){acquireFailed(threadId);returnfalse;}//进入死循环,反复调用tryAcquire尝试获取锁,和上段获取锁的逻辑一样while(true){longcurrentTime=System.currentTimeMillis();ttl=tryAcquire(leaseTime,unit,threadId);//lockacquiredif(ttl==null){returntrue;}time-=(System.currentTimeMillis()-currentTime);if(time<=0){acquireFailed(threadId);returnfalse;}//waitingformessagecurrentTime=System.currentTimeMillis();if(ttl>=0&&ttl