ReentrantLock和Synchronized是Java开发中最常用的锁。与SynchronizedJVM内置锁不同,ReentrantLock提供了更丰富的语义。可以创建公平锁或非公平锁,响应中断,等待超时,条件唤醒等。在某些场景下,使用ReentrantLock更合适也更强大。在前两篇文章中,我们分析了AQS的加锁过程和源码实现。我们当时说AQS采用模板设计模式,父类定义加锁流程,子类实现具体的加锁逻辑。所以大部分加锁的代码都在父类AQS中实现了,导致ReentrantLock的源码非常简单,一起来学习一下吧。先看看如何使用ReentrantLock?1.ReentrantLock的使用/***@authorOneLightArchitecture*@apiNoteReentrantLockExample**/publicclassReentrantLockDemo{publicstaticvoidmain(String[]args){//1.创建ReentrantLock对象ReentrantLocklock=newReentrantLock();//2.锁定lock.lock();try{//3.在这里执行具体的业务逻辑}finally{//4.释放锁lock.unlock();可以看到ReentrantLock的使用很简单,调用lock加锁,调用unlock释放锁,需要配置try/finally才能使用,保证代码执行失败时也能释放锁.ReentrantLock也可以与Condition条件结合使用。具体可以看前面几篇BlockingQueue的源码分析,里面有ReentrantLock的实际使用。再来看看ReentrantLock的类结构2.ReentrantLock类结构//实现Lock接口publicclassReentrantLockimplementsLock{//Sync同步变量只有一个privatefinalSyncsync;//Sync继承自AQS,主要逻辑在这里}可以看出ReentrantLock的类结构非常简单,实现了Lock接口。类中有两个静态内部类,分别实现了公平锁和非公平锁。看Lock接口,定义了哪些方法?publicinterfaceLock{//锁定voidlock();//添加一个可中断锁voidlockInterruptibly()throwsInterruptedException;//尝试锁定booleantryLock();//尝试锁定一段时间booleantryLock(longtime,TimeUnitunit)throwsInterruptedException;//释放锁voidunlock();//创建一个新的条件状态ConditionnewCondition();}是一些常用的使用锁的方法。在上一篇浏览AQS源码的时候,了解到AQS定义了一些加锁和释放锁的抽象方法,这些方法留给子类去实现。让我们看一下抽象方法://添加独占锁protectedbooleantryAcquire(intarg){thrownewUnsupportedOperationException();}//释放独占锁protectedbooleantryRelease(intarg){thrownewUnsupportedOperationException();}//添加共享锁protectedinttryAcquireShared(intarg){thrownewUnsupportedOperationException();}//释放共享锁protectedbooleantryReleaseShared(intarg){thrownewUnsupportedOperationException();}//判断当前线程是否持有lockprotectedbooleanisHeldExclusively(){thrownewUnsupportedOperationException();}由于使用了ReentrantLock是独占锁,所以只需要实现独占锁相关的方法即可。3.ReentrantLock源码分析3.1ReentrantLock构造方法//默认构造方法使用非公平锁publicReentrantLock(){sync=newNonfairSync();}//传true指定使用公平锁publicReentrantLock(booleanfair){同步=公平?newFairSync():newNonfairSync();}创建ReentrantLock对象时,可以指定使用公平锁还是非公平锁。默认是使用非公平锁。显然,非公平锁的性能更好。先想一个常见的面试题,公平锁和非公平锁是怎么实现的?3.2非公平锁源码先看锁源码:从父类ReentrantLock锁方法入口:publicclassReentrantLockimplementsLock{//锁入口方法publicvoidlock(){//在Sync.锁();}}子类NonfairSync中的Lock方法://非公平锁staticfinalclassNonfairSyncextendsSync{//Lockfinalvoidlock(){//1.先尝试加锁(使用CAS设置state=1)if(compareAndSetState(0,1))//2.如果加锁成功,则设置当前线程为持有锁的线程setExclusiveOwnerThread(Thread.currentThread());else//3.如果加锁不成功,则调用父类AQS中的实际加锁逻辑acquire(1);}}加锁逻辑也很简单,首先尝试使用CAS加锁(即设置状态从0到1),如果加锁成功,则当前线程设置为持有锁的线程。设计师很聪明。在锁竞争不激烈的情况下,大概率加锁成功,就不用再走else里复杂的加锁逻辑了。如果加锁不成功,还需要在else中调用父类AQS的acquire方法,acquire需要调用子类的tryAcquire方法。调用链接如下:根据调用链接,实际的加锁逻辑在Sync.nonfairTryAcquire方法中。abstractstaticclassSyncextendsAbstractQueuedSynchronizer{//非公平锁的最终加锁方式finalbooleannonfairTryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();//1.获取同步状态intc=getState();//2.state=0表示没有锁,先尝试获取锁(使用CAS设置state=1)if(c==0){if(compareAndSetState(0,acquires)){//3.如果成功获取锁后,将当前线程设置为持有锁线程setExclusiveOwnerThread(current);返回真;}//4.如果当前线程已经持有锁,则执行可重入逻辑}elseif(current==getExclusiveOwnerThread()){//5.锁数+获取intnextc=c+获取;//6.超过tnt类型的最大值,溢出if(nextc<0)thrownewError("Maximumlockcountexceeded");设置状态(下一步);返回真;}返回假;}}再看释放锁的调用过程。公平锁和非公平锁的过程是一样的,最后执行的是Sync.tryRelease方法:abstractstaticclassSyncextendsAbstractQueuedSynchronizer{//释放锁protectedfinalbooleantryRelease(intreleases){//1.同步状态减去锁释放次数intc=getState()-releases;//2.验证当前线程没有持有锁,报错if(Thread.currentThread()!=getExclusiveOwnerThread())thrownewIllegalMonitorStateException();布尔自由=假;//3.判断同步状态是否等于0,删除持有锁的线程if(c==0){free=true;setExclusiveOwnerThread(null);}设置状态(c);免费退货;}}再看公平锁的源码3.3公平锁的源码先看一下公平锁的加锁过程:final加锁方法是FairSync.tryAcquire,看具体逻辑:staticfinalclassFairSyncextendsSync{//实现父类的加锁逻辑protectedfinalbooleantryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();//1.获取同步状态intc=getState();//2.state=0表示不加锁,先尝试加锁(使用CAS设置state=1)if(c==0){//3.判断当前线程是否为head节点的下一个节点(先到先得)if(!hasQueuedPredecessors()&&compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);返回真;}//4.如果当前线程已经持有锁,则执行可重入逻辑}elseif(current==getExclusiveOwnerThread()){//5.锁数+获取intnextc=c+获取;//6.超过tnt类型的最大值,溢出if(nextc<0)thrownewError("Maximumlockcountexceeded");设置状态(下一步);返回真;}返回假;}//判断当前线程是否为头节点的下一个节点(先到先得)publicfinalbooleanhasQueuedPredecessors(){Nodet=tail;节点h=头;节点;返回h!=t&&((s=h.next)==null||s.thread!=Thread.currentThread());}}公平锁的锁释放逻辑和非公平锁的释放逻辑是一样的,上面说了4.总结看完了所有的ReentrantLock源码,是不是觉得ReentrantLock很简单。由于加锁过程的编排已经在父类AQS中实现,子类只需要实现具体的加锁逻辑即可。加锁逻辑也很简单,就是修改同步状态state和持有锁的线程exclusiveOwnerThread的值。
