源码分析:同步基础框架——AbstractQueuedSynchronizer(AQS) (3)

自旋,直到成功把新建的节点加入到同步队列;方法enq:
为什么要自旋呢?是因为在调这个方法的时候,可能有其他想要获得锁线程没有获得锁,并且已经修改了尾节点;

再次尝试从同步队列获得锁,方法acquireQueued :

上面已经把当前线程的节点加入到队列中了,理论上排队的线程很多的话,它是马上获取不到锁的

所以它会自旋判断是否到了自己可以获取锁和CAS尝试获取锁,关键代码if (p == head && tryAcquire(arg))
理论上当前线程进入到了队列排队,只要队列中还有更早的线程在它前面排队,当前线程都不会比更早的线程先获得锁,所以在这一块对于公平锁和非公平锁肯定都是公平的。

没有资格去获取锁或没有成功获得锁,就阻塞自己,方法parkAndCheckInterrupt

阻塞线程被唤醒,自旋成功获得锁,排队期间被中断的线程也会获得锁,之后退出自旋循环,返回线程的中断状态;

线程如果被中断了,中断当前线程,被中断的线程还是会继续执行后面逻辑

以上过程涉及到的技术点有:CAS,自旋,队列入队,队列删除节点(被取消的节点),阻塞线程(LockSupport.park(this))

释放锁:public final boolean release(int arg)

unlock方法调用的是sync.release(1),而release是AQS 方法中的方法,表示将同步状态设置回初始状态,将锁释放。

public final boolean release(int arg) { // tryRelease 是我们自己的实现,就是把state字段设置成0,如果是可重入的,只能慢慢减到初始状态 if (tryRelease(arg)) { // 进入到这里说明CAS 设置成功,也就代表锁成功释放了,需要唤醒队列中的第一个排队的节点线程 Node h = head; // head 表示的是当前获得锁的节点 if (h != null && h.waitStatus != 0) // 唤醒头结点的下一个节点 unparkSuccessor(h); return true; } return false; } private void unparkSuccessor(Node node) { // 这里的node 是当前持有锁的节点 int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); // 找到头结点的后继节点 Node s = node.next; if (s == null || s.waitStatus > 0) { // 大于0的状态只有1,表示被取消了,如果被取消了,就继续取下一个节点唤醒 s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) **LockSupport.unpark(s.thread);** }

释放锁的逻辑比加锁逻辑要简单很多,主要逻辑有:

修改同步状态为初始值,方法tryRelease(arg):

这里我们自己实现的会直接将同步状态设置为0,如果是支持可重入,就需要慢慢减了;

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

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