ReentrantLock源码探究1:非公平锁的获取和释放 (3)

该部分代码可以用现实中排队办理业务的情况来说明:

假设你排队去办理业务,队伍很长,因此除了当前正在办理业务的人,其他所有排队的人都在低头玩手机,且每个排队的人有以下三种状态:①.正常排队,且办完业务后会通知后面的人别玩手机了可以开始办理业务了。②.发现队伍过长,不排队了,走了。③.正常排队,办完业务后不通知后面的人,直接走。 此时你进入该队伍的尾部开始排队。 1.第一步,判断排队在你前面的人是否会通知你,如果会通知,那么我们就可以不用关心其他问题,在队列中待着玩手机即可。 2.第二步,如果发现排在你前面的人不排队了,要走了,那么此时我们就得往前走一位,并开始不断询问前面的人是不是也准备不排队了,直到我们排在了一个确定不会走的人后面。 3.第三步,排在你前面的人不是准备走的,但是他也不会通知你,那么你就要告诉他,一定得在办完业务后通知你。

当我们确定我们已经在队列中待好后(前置会通知我们),那么我们就可以开始休息。parkAndCheckInterrupt方法让我们的线程进入等待的状态,即休息状态。

private final boolean parkAndCheckInterrupt() { LockSupport.park(this); //调用park()使线程进入waiting状态 return Thread.interrupted(); //如果被唤醒,查看自己是不是被中断的。 }

ReentrantLock源码探究1:非公平锁的获取和释放

3.锁的释放过程 public void unlock() { sync.release(1); } public final boolean release(int arg) { if (tryRelease(arg)) { //若tryRelease后无人占用锁 Node h = head; //获取队列的头结点h if (h != null && h.waitStatus != 0) //若h非空,且h的waitStatus不为0 unparkSuccessor(h); //唤醒后继 return true; } return false; } protected final boolean tryRelease(int releases) { int c = getState() - releases; //当前state-1,得到c if (Thread.currentThread() != getExclusiveOwnerThread()) //执行releas的不是获取锁的独占线程,抛出异常 throw new IllegalMonitorStateException(); boolean free = false; //free用来标记锁是否可获取状态 if (c == 0) { //若state=0 free = true; //那么当前锁是可获取的 setExclusiveOwnerThread(null); //设置当前锁的独占线程为null } setState(c); //设置当前state为c return free; //返回锁是否是可获取状态 } private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; //获取当前线程对应节点的waitStatus if (ws < 0) //将当前线程对应节点waitStatus置为0 compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; //获取当前线程对应节点的后继节点s if (s == null || s.waitStatus > 0) { //若s为空或s的状态是canceled s = null; //将s设置为null。 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) //此处从尾到头进行遍历,找到队列最前列的节点且状态不是Canceled,将其设置为s。但此处为何从尾部开始遍历尚未弄清楚。 s = t; } if (s != null) //若上述遍历找到的s非空 LockSupport.unpark(s.thread); //调用lockSupport.unpark唤醒s对应的线程 }

release方法的逻辑仍然可以用一个办理完业务的人的后续动作来进行说明:

1.若A办理业务后无其他业务需要办理,那么表示当前业务窗口是free的。 2.A将自己的等待状态置为0,相当于退出队列。然后检查自己后面的人是否是空或者取消排队的状态。若为真,将后置设为空。 3.从队列的尾部遍历到头部,直到找到队列最前头的那个,且它的等待状态不是取消状态,那么将其唤醒,告知他可以开始办理业务了。 4.关于源码的一点疑问

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyfwsp.html