源码分析上一节带大家学习了它的基本使用,我们可以理解为是一个可重入锁,下面我们来看看它的底层实现吧~我们在构造函数中使用它的时候,它首先是new,所以我们先看它的构造函数。它有两个主要功能:publicReentrantLock(){sync=newNonfairSync();}publicReentrantLock(booleanfair){sync=fair?newFairSync():newNonfairSync();}从字面上看,它们之间的区别是公平的,翻译过来就是公平的意思。一般可以猜到它是用来构建公平锁和非公平锁的。继续往下看源码之前,先给大家科普一下这两款锁。公平锁&非公平锁公平锁多个线程按照申请锁的顺序获取锁。线程会直接进入队列排队,永远是队列中最先获得锁的。(比如银行做业务取号)。这种锁的优点是显而易见的。每个线程都可以获取资源,但是缺点也很明显。如果一个线程被阻塞,其他线程也会被阻塞。然而,CPU唤醒成本非常高,正如我之前告诉你的。非公平锁多个线程尝试获取锁,如果获取失败则进入等待队列,不需要唤醒CPU。优缺点正好与上面相反。优点是减少了开销,缺点也很明显。可能会导致锁获取不到或者长时间获取不到。好了,有了基本概念之后,我们继续往下看。NonfairSync首先,让我们看一下非公平锁。默认情况下,我们申请的是非公平锁,即newReentrantLock()。让我们看一下源代码。staticfinalclassNonfairSyncextendsSync{privatestaticfinallongserialVersionUID=7316153563782823691L;/***执行锁定。尝试立即插入,备份到正常*失败时获取。*/finalvoidlock(){if(CompareAndSet))setExclusiveOwnerThread(Thread.currentThread());否则获取(1);}protectedfinalbooleantryAcquire(intacquires){returnnonfairTryAcquire(acquires);}}继承自Sync,是一个内容静态抽象类:abstractstaticclassSyncextendsAbstractQueuedSynchronizer{...}分为公平和不公平,用AQS状态来表示持有锁的次数,有sync=...构造函数初始化完成后,我们再来看下NonfairSync。在使用的时候,我们调用了lock.lock()方法,它是ReentrantLock的一个实例方法。//获取锁publicvoidlock(){sync.lock();}其实sync的内部方法内部还是有调整的,因为我们申请的是非公平锁,所以我们看一下NonfairSync下的锁实现:finalvoidlock(){if(compareAndSetState(0,1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}compareAndSetState是AQS的一个内部方法,意思是如果当前状态值等于期望值,会自动同步状态设置为给定的更新值。此操作具有易失性读写的内存语义。protectedfinalbooleancompareAndSetState(intexpect,intupdate){//请参阅下面的内在设置以支持此returnunsafe.compareAndSwapInt(this,stateOffset,expect,update);}可以看到lock方法的执行将计数通过AQS机制,setExclusiveOwnerThread设置线程独占访问权限,是AbstractOwnableSynchronizer的内部方法,子类使用它来管理线程独占访问。publicabstractclassAbstractQueuedSynchronizerextendsAbstractOwnableSynchronizerimplementsjava.io.Serializable{}可以看到它继承了AbstractOwnableSynchronizer。我们接下来看,我们说如果实际值等于期望值,就会执行上面的方法,不期望的时候会执行acquire(1)。publicfinalvoidacquire(intarg){if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}此方法以独占模式获取,忽略中断,它将尝试调用tryAcquire,成功则返回,不成功则进入线程队列,可以反复阻塞和解阻塞。看看AQS里面的这个方法。protectedbooleantryAcquire(intarg){thrownewUnsupportedOperationException();}我们可以看到肯定不是这里的实现,它的具体实现在NonfairSync中。protectedfinalbooleantryAcquire(intacquires){returnnonfairTryAcquire(acquires);您可以看到它调用了nonfairTryAcquire方法。这个方法是一个不公平的tryLock。具体实现在Sync里面。这里需要重点说一下。finalbooleannonfairTryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();//返回同步状态值,这是AQS内部的一个方法//privatevolatileintstate;//protectedfinalintgetState(){//返回状态;//}intc=getState();if(c==0){//如果是0,比较。如果与期望值相同,则设置为独占线程,说明已经获得锁if(compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);返回真;}}//否则,判断当前线程是否已经设置为独占线程elseif(current==getExclusiveOwnerThread()){//设置当前线程状态值+1,返回成功intnextc=c+acquires;if(nextc<0)//溢出thrownewError("Maximumlockcountexceeded");设置状态(下一步);返回真;}//否则返回失败,没有获取到锁returnfalse;}好吧,让我们再回顾一下acquire。publicfinalvoidacquire(intarg){//如果当前线程没有获取到锁,队列中的线程不断尝试获取锁,如果被中断则返回true,调用selfInterruptif(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}selfInterrupt很好理解,线程中断。staticvoidselfInterrupt(){Thread.currentThread().interrupt();}其实我们关注的重点是acquireQueued这个方法,首先关注入参,它内部传入了一个addWaiter,最后返回到NODE节点。privateNodeaddWaiter(Nodemode){//mode没什么好说的,是独占模式的标志staticfinalNodeEXCLUSIVE=null;节点node=newNode(Thread.currentThread(),mode);//尝试enq的快速路径;在节点pred=tail发生故障时备份到完整enq;if(pred!=null){node.prev=pred;if(compareAndSetTail(pred,node)){pred.next=node;返回节点;}enq(节点);返回节点;}我们可以大致猜想Node是一个等待队列的节点类,它是一个链表结构。之前我们讲FutureTask的源码的时候也遇到过这种结构。它通常用于自旋锁,在这个地方,它用于阻塞同步器。+-----+上一个+-----++-----+head||<----||<----||tail+-----++-----++-----+好吧,让我们关注acquireQueued。finalbooleanacquireQueued(finalNodenode,intarg){booleanfailed=true;try{//默认为falsebooleaninterrupted=false;//进入阻塞循环遍历线程队列for(;;){//返回上一个节点finalNodep=node.predecessor();//判断前一个节点是否为头节点并获得锁,则当前节点将被设置为头节点if(p==head&&tryAcquire(arg)){setHead(node);//这里可以看到注释helpgc,p.next=null;//帮助GCfailed=false;返回中断;}//检查并更新故障节点的状态。如果线程应该阻塞并且线程被中断则返回trueif(shouldParkAfterFailedAcquire(p,node)&&parkAndCheckInterrupt())interrupted=true;}}finally{//如果失败取消你试图获取的节点if(failed)cancelAcquire(node);}}从上面的源码中,经历了上面说的非公平锁的概念,是不是比较好理解,然后释放锁unlock,可以看出这个方法是ReentrantLock下的一个实例方法,所以,释放锁的方法公平锁也进行了调整。其实可以猜到最后调用的是sync方法。publicvoidunlock(){sync.release(1);}sync继承AQS,release是AQS的内部方法。publicfinalbooleanrelease(intarg){//尝试释放锁tryReleaseinsideSyncif(tryRelease(arg)){Nodeh=head;//如果节点存在且state值不为0if(h!=null&&h.waitStatus!=0)//唤醒下一个节点unparkSuccessor(h);返回真;}返回假;}privatevoidunparkSuccessor(Nodenode){intws=node.waitStatus;如果(ws<0)compareAndSetWaitStatus(节点,ws,0);节点s=node.next;如果(s==null||s.waitStatus>0){s=null;for(Nodet=tail;t!=null&&t!=node;t=t.prev)if(t.waitStatus<=0)s=t;}if(s!=null)//可以看到调用了LockSupport唤醒LockSupport.unpark(s.thread);让我们看看tryRelease,Sync中的相同实现。protectedfinalbooleantryRelease(intreleases){//释放锁时也使用AQS计数intc=getState()-releases;//判断当前线程是否为独占线程,不抛异常if(Thread.currentThread()!=getExclusiveOwnerThread())thrownewIllegalMonitorStateException();布尔自由=假;//如果为0,表示释放成功if(c==0){free=true;//并将独占线程设置为空setExclusiveOwnerThread(null);}//更新状态值setState(c);免费退货;}FairSync公平锁FairSync的不同之处在于其获取锁的实现在其内部,而Sync默认在内部实现了非公平锁。staticfinalclassFairSyncextendsSync{privatestaticfinallongserialVersionUID=-3000897897090466540L;//这个方法最终调用了tryAcquirefinalvoidlock(){acquire(1);}//公平锁实现protectedfinalbooleantryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();intc=getState();//这个有点类似于非公平锁的实现,也是判断状态if(c==0){//判断排队队列是否存在并比较期望值if(!hasQueuedPredecessors()&&compareAndSetState(0,acquires)){//设置独占线程并返回成功setExclusiveOwnerThread(current);返回真;}}//这和上面的类似elseif(current==getExclusiveOwnerThread()){intnextc=c+acquires;if(nextc<0)thrownewError("超过最大锁计数");设置状态(下一步);返回真;}返回假;}}它的实现比较简单。通过实现可以发现,它是按照申请锁的顺序获取锁的。一段时间后,releaselockunlock就很容易理解了,这个我们上面已经讲过了。结语这一节的内容可能会很多。主要还是看源码。可以自己打断调整,举个例子学习一下,结合源码了解一下什么是公平锁和非公平锁,ReentrantLock可重入锁体验在哪里。
