传入参数为true,是公平锁,false为非公平锁。

 我们这边解读源码以非公平锁为例来解读:

 compareAndSetState(0, 1)通过CAS设置state状态为1,如果设置成功则加锁成功,设置当前拥有独占访问权限的线程为当前访问线程。后面没有设置成功的线程执行else中的acquire(1)方法。

进入acquire(1)方法中,这个方法中包含了线程入队,尝试拿锁,LockSupport的park()阻塞线程。 

 首先我们先看下tryAcquire(arg)方法,这个方法会调用nonfairTryAcquire(int acquires),尝试去拿锁。

 如果tryAcquire(arg)返回为true,取反!tryAcquire(arg)为true,继续往下走,执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg))中的addWaiter(Node.EXCLUSIVE)方法,为当前线程和给定模式创建节点并将其排队。

  刚开始的时候是第一次进来,所以会走到enq(node)中,如果尾节点为空,则新建一个node节点作为一个傀儡节点,并被头节点指向,然后头,尾节点都指向傀儡节点,但它是处在自旋死循环中,所以又执行一遍,又执行一遍以后,尾节点不为空,执行下面else方法,将当前节点的prev指向原来的tail,当前尾指针执行当前节点,原来的tail节点next指向当前节点。

 

 如果tail节点不为空,那我们把节点加入到队列里面。如下图逻辑注释所示。

 然后执行acquireQueued(final Node node, int arg)方法,如下图所示。

 

 

 

 当前节点的前一个节点是否等于头节点并且尝试去拿锁,如果满足这两个条件,先把傀儡节点引用指针去掉,使其不被其他节点引用,可以被GC回收掉。然后把当前节点变为傀儡节点。shouldParkAfterFailedAcquire(p, node)方法首次进来的时候,当前节点的前一个节点是傀儡节点,傀儡节点的waitStatus状态为0,会走else的逻辑,通过CAS吧waitStatus值设为-1。

 返回false以后,继续在死循环中执行,继续执行shouldParkAfterFailedAcquire(p, node)方法,这个时候ws的值为-1了,if (ws == Node.SIGNAL)为true,进入判断里面返回true.

 

 

 返回true以后,会执行parkAndCheckInterrupt()方法使线程等待。

 

 

 

 parkAndCheckInterrupt()方法执行到LockSupport.park(this)时,会让当前线程处于等待的状态,直到其他线程唤醒它。

 

 

 

接着我们看一下unlock()方法。

 

 进入release(int arg)方法,主要是设置state状态和唤醒等待的线程,等待的线程就可以继续走它下面的逻辑了.

 

 

唤醒以后,我们再回到acquireQueued(final Node node, int arg)中,parkAndCheckInterrupt()等待的线程被唤醒,继续执行死循环里面的逻辑,当前节点前一个节点是head节点,调用tryAcquire(arg)又尝试去拿锁。

 

 

进入尝试拿锁方法里面,如下图所示,因为这个时候state已经在unlock()的时候被改为0了,所以这个被唤醒的线程能够CAS设置state的值为1,等于抢到了这把锁。

 

 抢到锁以后,返回true,把傀儡节点引用指针去掉,使其不被其他节点引用,可以被GC回收掉,然后把当前节点变为傀儡节点.然后返回false,调用方法结束。

 

 

posted on 2021-02-03 16:57  路飞_lufei  阅读(53)  评论(1编辑  收藏  举报