众所周知:ReentrantLock可以是公平的也可以是非公平的。
所谓公平:就是先来的肯定比后来的先获取到锁。也就是不能插队。
所谓非公平:就是说,后来的可以比先来的先获取到锁,也就是说可以插队。
ReentrantLock reentrantLock = new ReentrantLock(true);//公平ReentrantLock reentrantLock = new ReentrantLock();//非公平
以公平为例子进行讲解。
ReentrantLock reentrantLock = new ReentrantLock(true);try{reentrantLock.lock();//业务逻辑}catch (Exception e){e.printStackTrace();}finally {reentrantLock.unlock();}
我们知道,ReentrantLock 使用起来就向上面那样。一个lock一个unlock即可。
首先看lock方法:
public void lock() {sync.lock();}
继续跟进去,最后可以看到,调用的是acquire(1):
final void lock() {acquire(1);}
acquire方法:
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
重点从这里才开始,上面的都是依次跟就行。
首先进行了一个if判断。
第一个方法:tryAcquire
protected final boolean tryAcquire(int acquires) {//这里是公平锁的tryAcquire方法final Thread current &#61; Thread.currentThread();//这里能拿到state变量&#xff0c;这个变量是volatile修饰的&#xff0c;为的是一旦修改立即可见int c &#61; getState();//如果c为0&#xff0c;此刻表示还没有线程获取到锁if (c &#61;&#61; 0) {//hasQueuedPredecessors方法先去查看阻塞队列中是否有节点在等待&#xff0c;这里也是体现公平锁的地方if (!hasQueuedPredecessors() &&//通过CAS的方式进行设置state为1compareAndSetState(0, acquires)) {//设置独占锁的线程为当前线程setExclusiveOwnerThread(current);//返回true&#xff0c;表示获取锁成功了return true;}}//这里是可重入的体现&#xff0c;如果当前线程就是获取了独占锁的线程else if (current &#61;&#61; getExclusiveOwnerThread()) {//那么也就是将state&#43;1,也就是表示获取state&#43;1次锁int nextc &#61; c &#43; acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);//此时也是获取锁成功return true;}//如果c!&#61;0并且获取锁的独占线程也不是当前线程&#xff0c;那么就返回false&#xff0c;也就是说获取锁失败了return false;}
这个方法源码虽然很短&#xff0c;但是含义却很多。
这里要先简单介绍一下这个Node&#xff1a;
这个Node里面现在只要知道里面有prev、next、thread
可以暂时理解为&#xff1a;
clss Node{//指向前一个节点Node prev;//指向下一个节点Node next;//创建当前Node的线程Thread thread;
}
Node里面当然不只是这几个属性&#xff0c;但是先这么简单的理解一下。
public final boolean hasQueuedPredecessors() {// The correctness of this depends on head being initialized// before tail and on head.next being accurate if the current// thread is first in queue.Node t &#61; tail; // Read fields in reverse initialization orderNode h &#61; head;Node s;//如果此时h&#61;&#61;t&#xff0c;说明此时队列中没有节点&#xff0c;可以是null&#61;&#61;null或者 就只有一个虚拟节点//此时如果h!&#61;t&#xff0c;说明此时队列中有至少一个节点&#xff0c;说明可能要排队return h !&#61; t &&//s表示第二个节点&#xff0c;此时如果s&#61;&#61;null&#xff0c;表示队列中只有一个节点&#xff0c;但是出现了h与t不相等的情况。//比如&#xff1a;在定义完Node s这行代码后&#xff0c;当前线程的时间片用完了&#xff0c;此时另一个线程t2开始执行//假设t2线程执行到 enq方法中的compareAndSetHead(new Node())&#xff0c;执行完了这行代码&#xff0c;然后t2的额时间片也用完了//此时又轮到t1开始执行了&#xff0c;此时h已经被初始化了&#xff0c;但是t还是null//此时h!&#61;t//但是下面这行代码&#xff0c;s&#61;&#61;null是true&#xff0c;此时说明不止当前线程在获取锁&#xff0c;并且有线程已经比当前线程更加快的执行了//此时当前线程选择加入队列&#xff0c;所以s&#61;&#61;null&#xff0c;并且这个方法返回true//如果s!&#61;null&#xff0c;那么判断当前线程是不是排队的第一个Node节点//如果当前线程不是第一个节点&#xff0c;那么当前线程的这个节点就去排队&#xff0c;//什么时候s.thread&#61;&#61;Thread.currentThread()&#xff1f;//当 当前线程是被另一个释放锁的线程unpack唤醒的时候&#xff0c;s.thread&#61;&#61;Thread.currentThread()((s &#61; h.next) &#61;&#61; null || s.thread !&#61; Thread.currentThread());}
到此&#xff0c;tryAcquire方法分析完毕。
然后返回到acquire方法。
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
如果当tryAcquire(arg)方法返回false&#xff0c;取反&#xff0c;那就是true----也就是获取锁失败。
此时进入第二个条件。
此时我们也可以自己简单分析到&#xff0c;现在锁被别的线程占了&#xff0c;那么当前线程应该等待锁释放&#xff0c;那么自然就应该是加入等待队列。
等待队列中&#xff0c;排队的不是线程&#xff0c;而是上面提到的Node节点。
所以接下来第一步&#xff0c;应该是得到可以代表当前thread的Node节点。
第二步&#xff0c;应该是将这个节点加入到等待队列中。
第三步&#xff0c;应该是释放CPU&#xff0c;进行等待。
接下来我们通过分析源码&#xff0c;看看是不是上面的这种思路。
private Node addWaiter(Node mode) {//CAS进行尾插法//如果CAS失败了&#xff0c;那么就进入enq方法进行备份//初始化新节点Node node &#61; new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failure//记录原来的尾巴Node pred &#61; tail;if (pred !&#61; null) {//尾插法node.prev &#61; pred;//CAS进行更新尾部if (compareAndSetTail(pred, node)) {pred.next &#61; node;return node;}}//此时说明CAS失败了或者队列没有进行初始化enq(node);return node;}
此时如果上面的tail为null&#xff0c;或者CAS设置tail失败了&#xff0c;那么就要进入enq方法。
/*** Inserts node into queue, initializing if necessary. See picture above.* &#64;param node the node to insert* &#64;return node&#39;s predecessor*/private Node enq(final Node node) {//此时说明CAS失败了或者队列没有进行初始化//进行自旋的CAS进行插入&#xff0c;尾插法for (;;) {Node t &#61; tail;if (t &#61;&#61; null) { // Must initialize//此时说明队列没有进行初始化&#xff0c;开始初始化if (compareAndSetHead(new Node()))tail &#61; head;} else {//CAS进行尾插法node.prev &#61; t;if (compareAndSetTail(t, node)) {t.next &#61; node;return t;}}}}
通过上面的分析&#xff0c;进入这个enq方法有两种情况。
**情况一&#xff1a;**这个等待队列还没有被初始化。那么此时的情况是这样的。如下图。
那么此时源码里面的t就是为null&#xff0c;此时就会 进入第一个if&#xff0c;利用CAS设置head&#xff0c;并且此时是要将head设置为一个新得Node节点。如下图。
此时CAS完毕&#xff0c;源码执行了tail&#61;head.如图。
此时设置head和tail完毕&#xff0c;也就是初始化这个队列完毕了&#xff0c;但是因为此时是出于一个死循环中&#xff0c;所以进行下一次循环。
此时还是t&#61;tail&#xff0c;不过此时t不为null&#xff0c;所以此时进入else。如图。
此时执行完毕。
情况二&#xff1a;是在addWaiter里面进行CAS失败了。
那么说明此时队列里面不为null&#xff0c;因为CAS失败&#xff0c;也就是有其它线程改变了tail。
那么此时的情况应该是如下图。
这是进入enq的时候&#xff0c;队列的情况&#xff0c;可能里面已经有了不止一个Node节点&#xff0c;可能有很多&#xff0c;图里面有一个节点表示一下&#xff0c;讲到这里&#xff0c;有个地方要补充一下。
队列初始化完以后&#xff0c;head指向的节点的thread一定为null。
所以上面 说队列中有一个节点的意思是除了head指向的那个节点以外的节点还有一个。
此时进入死循环&#xff0c;此时t&#61;tail&#xff0c;肯定不为null了&#xff0c;那么直接进入else&#xff0c;如图。
此时node节点也就加入了队列了。
此时enq方法返回—》addWaiter方法返回—》进入acquireQueued方法
此时要注意一下&#xff0c;addWaiter方法返回的是node节点。
到此为止和我们上面的分析差不多。
做了这么几件事&#xff0c;得到能代表当前线程的node节点&#xff0c;并且将这个节点加入到等待队列中。
那么下面应该干嘛呢&#xff1f;按道理说&#xff0c;应该是等待了&#xff0c;也就是当前线程释放cpu了。好&#xff0c;我们继续看。
/*** Acquires in exclusive uninterruptible mode for thread already in* queue. Used by condition wait methods as well as acquire.** &#64;param node the node* &#64;param arg the acquire argument* &#64;return {&#64;code true} if interrupted while waiting*/final boolean acquireQueued(final Node node, int arg) {//node 即将要park的节点//真正park前&#xff0c;还要进行两次尝试获取锁boolean failed &#61; true;try {boolean interrupted &#61; false;for (;;) {//拿到node前一个节点final Node p &#61; node.predecessor();//如果此时p就是head&#xff0c;说明此时node就是排队的第一个//只有当node是排队的第一个的时候&#xff0c;才有机会去竞争锁if (p &#61;&#61; head && tryAcquire(arg)) {//这里表示竞争到了&#xff0c;将第一个节点设置为头节点&#xff0c;即虚拟节点//虚拟节点的Threa永远是nullsetHead(node);p.next &#61; null; // help GCfailed &#61; false;return interrupted;}//shouldParkAfterFailedAcquire设置p的ws为-1&#xff0c;表示p后面还有节点排队&#xff0c;并且也再给了一次机会给node去尝试获取锁if (shouldParkAfterFailedAcquire(p, node) &&//parkAndCheckInterrupt真正的去park线程&#xff0c;当前线程//parkAndCheckInterrupt返回的是当前线程的被中断状态parkAndCheckInterrupt())//如果上面parkAndCheckInterrupt返回的是true&#xff0c;说明这个线程被中断过&#xff0c;记录一下&#xff0c;后面会返回interrupted &#61; true;}} finally {if (failed)//如果获取锁失败饿了&#xff0c;也就是上面那个死循环不是获取锁程过退出的&#xff0c;而是异常推出的&#xff0c;那么就将之前插入的节点进行取消//这里的取消就是将队列中原本就是取消状态的移出队列cancelAcquire(node);}}
这里也是一个死循环。
上面说到&#xff0c;head指向的节点thread一定是null&#xff0c;那么也就是说&#xff0c;如果等到队列中有节点在等待&#xff0c;一旦释放锁&#xff0c;那么下一个得到锁的应该是head指向的那个节点的&#xff0c;下一个节点。
换句话说&#xff0c;下一次得到锁的&#xff0c;应该是prev是head的节点。
源码中
final Node p &#61; node.predecessor();if (p &#61;&#61; head && tryAcquire(arg)) {
这两行代码也就是在做这个事情&#xff0c;p就是node的前一个节点&#xff0c;如果node的节点就是head&#xff0c;那么node节点也就是在释放锁后下一个应该得到锁的节点。
所以此时当前线程又尝试去获取锁&#xff0c;可以理解为&#xff0c;我就是排队的第一个&#xff0c;我去问问&#xff0c;是不是到我了。
tryAcquire方法上面讲了&#xff0c;这里就不重复讲了。
那么这里就有两种情况&#xff0c;如果此时尝试获取锁&#xff0c;获取到了。还有就是没获取到。
情况一&#xff1a; 获取到了锁。
那么我们也可以自己分析一下。
获取到了锁&#xff0c;那下一步&#xff0c;应该是出队。
不过这里的具体实现的做法是&#xff1a;
将当前节点设置为head&#xff0c;也就是setHead方法。可以进去看一下&#xff1a;
private void setHead(Node node) {head &#61; node;node.thread &#61; null;node.prev &#61; null;}
可以看到&#xff0c;修改了head为node ,并且当thread置为null&#xff0c;也就应了上面说的&#xff0c;head指向的节点中的thread一定为null。然后prev置为null&#xff0c;因为此时node就是head&#xff0c;所以prev肯定就null了。
然后p.next&#61;null也就是将之前的头节点的next指针指向null。
源代码的作者写的注释是help GC。
然后修改了一个变量failed为false。这说明获取锁成功了。
这里返回的interrupted是个boolean类型&#xff0c;这个-------------------
情况二&#xff1a; 尝试获取锁失败了
那么应该进入下一个if
/*** Checks and updates status for a node that failed to acquire.* Returns true if thread should block. This is the main signal* control in all acquire loops. Requires that pred &#61;&#61; node.prev.** &#64;param pred node&#39;s predecessor holding status* &#64;param node the node* &#64;return {&#64;code true} if thread should block*/private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//获取前一个节点的等待标志//第一次进来&#xff0c;这个waiteStatus是Node里面的一个属性&#xff0c;不过从上面源码一直到这里&#xff0c;我们可没有设置过&#xff0c;所以他就是初始化的值0,这里要注意&#xff0c;这个ws拿的是pred的&#xff0c;也就是前一个节点的。int ws &#61; pred.waitStatus;//Node.SIGNAL为-1 如果前一个状态为-1&#xff0c;那么线程这个线程真的要被park了。//第一次进来&#xff0c;ws为0&#xff0c;不会进这个ifif (ws &#61;&#61; Node.SIGNAL)/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;//第一次进来&#xff0c;ws为0,不会进这个ifif (ws > 0) {/** Predecessor was cancelled. Skip over predecessors and* indicate retry.*///说明前一个节点被取消了&#xff0c;那么更新要给这个队列do {node.prev &#61; pred &#61; pred.prev;} while (pred.waitStatus > 0);pred.next &#61; node;} else {/** waitStatus must be 0 or PROPAGATE. Indicate that we* need a signal, but don&#39;t park yet. Caller will need to* retry to make sure it cannot acquire before parking.*///设置前一个节点的等待状态//第一次进来&#xff0c;因为ws为0&#xff0c;不会进入上面的条件&#xff0c;所以进到这里//此时是将前一个节点的WaitStatus设置为-1&#xff0c;也就是Node.SIGNALcompareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}
然后此时shouldParkAfterFailedAcquire方法退出&#xff0c;返回false。
此时也就是返回到了acquireQueued。
if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted &#61; true;
此时这个if第一个条件返回了false&#xff0c;所以不会继续判断第二个条件&#xff0c;但是当前是处在一个死循环中&#xff0c;所以进行下一轮循环。
又来一遍&#xff0c;获取当前节点的前一个节点p&#xff0c;然后再判断前一个节点是不是head&#xff0c;如果是&#xff0c;那就再去尝试获取一次锁。。。。。。
然后又到了这个shouldParkAfterFailedAcquire
/*** Checks and updates status for a node that failed to acquire.* Returns true if thread should block. This is the main signal* control in all acquire loops. Requires that pred &#61;&#61; node.prev.** &#64;param pred node&#39;s predecessor holding status* &#64;param node the node* &#64;return {&#64;code true} if thread should block*/private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//获取前一个节点的等待标志//第一次进来&#xff0c;这个waiteStatus是Node里面的一个属性&#xff0c;不过从上面源码一直到这里&#xff0c;我们可没有设置过&#xff0c;所以他就是初始化的值0,这里要注意&#xff0c;这个ws拿的是pred的&#xff0c;也就是前一个节点的。int ws &#61; pred.waitStatus;//Node.SIGNAL为-1 如果前一个状态为-1&#xff0c;那么线程这个线程真的要被park了。//第一次进来&#xff0c;ws为0&#xff0c;不会进这个if//第二次进来&#xff0c;因为第一次已经将pred.waitStatus设置为了Node.SIGNAL&#xff0c;所以此时会返回trueif (ws &#61;&#61; Node.SIGNAL)/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;//第一次进来&#xff0c;ws为0,不会进这个ifif (ws > 0) {/** Predecessor was cancelled. Skip over predecessors and* indicate retry.*///说明前一个节点被取消了&#xff0c;那么更新要给这个队列do {node.prev &#61; pred &#61; pred.prev;} while (pred.waitStatus > 0);pred.next &#61; node;} else {/** waitStatus must be 0 or PROPAGATE. Indicate that we* need a signal, but don&#39;t park yet. Caller will need to* retry to make sure it cannot acquire before parking.*///设置前一个节点的等待状态//第一次进来&#xff0c;因为ws为0&#xff0c;不会进入上面的条件&#xff0c;所以进到这里//此时是将前一个节点的WaitStatus设置为-1&#xff0c;也就是Node.SIGNALcompareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}
第二次进来&#xff0c;直接进了第一个if&#xff0c;返回true.那么此时又返回到acquireQueued。
if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted &#61; true;
此时第一个条件返回了true&#xff0c;那么继续判断第二个条件。
private final boolean parkAndCheckInterrupt() {//此时park了&#xff0c;释放了cpu&#xff0c;下一次被唤醒也是从这里开始继续执行&#xff0c;这点要记得。LockSupport.park(this);return Thread.interrupted();}
到此&#xff0c;lock成功与不成功分析完毕。
这里再说一个&#xff0c;就是这个Thread.isinterrupted方法。
可以继续跟一下。
A thread interruption ignored because a thread was not alive* at the time of the interrupt will be reflected by this method* returning false.** &#64;return /*** Tests whether the current thread has been interrupted. The* interrupted status of the thread is cleared by this method. In* other words, if this method were to be called twice in succession, the* second call would return false (unless the current thread were* interrupted again, after the first call had cleared its interrupted* status and before the second call had examined it).**
true
if the current thread has been interrupted;* false
otherwise.* &#64;see #isInterrupted()* &#64;revised 6.0*/
再根就是一个nvtive方法了&#xff0c;不过这里的注释写的很清楚&#xff0c;这个方法实现了&#xff1a;
返回当前线程是否处于中断状态。
如果处于中断状态&#xff0c;那么这个状态会被重置为运行状态。返回true。
如果处于运行状态&#xff0c;直接返回false。
说到这里&#xff0c;就不得不再说一个和它类似的方法了。
thread.isInterrupted()
这个方法跟一下&#xff1a;
A thread interruption ignored because a thread was not alive* at the time of the interrupt will be reflected by this method* returning false.** &#64;return /*** Tests whether this thread has been interrupted. The interrupted* status of the thread is unaffected by this method.**
true
if this thread has been interrupted;* false
otherwise.* &#64;see #interrupted()* &#64;revised 6.0*/
这个方法就是单纯的返回线程是否处于中断状态。不会去清除中断状态。