aqs 同步队列的实现
Lock lock = new ReentrantLock();
lock.lock();
lock.unlock();
首先: lock 加锁的时时序图
![8676cc2612c64d7ffb6bb37fe190f168.png](https://i-blog.csdnimg.cn/blog_migrate/c1cfc46c07565b223f17a55ed543512d.png)
线程A , 线程B 争抢锁, 总有一个线程能正确成功。 并将锁的次数标志 State= +1 , State标识锁的次数。
并成功释放执行 unlock() ,将 State 设置位0;
第一步:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
compareAndSetState(0,1) 原子性操作, 会将state 的状态由0-> 1.。 即标识锁了1次。 设置成功后会当前
exclusiveOwnerThread = thread;
如果线程 b 正确 第一步失败, 则会进入 acquire(1);
第二步:
tryAcquire(int acquires) , 改方法调用 nonfairTryAcquire(int acquires),
![2fd1bb2b820f4b4aba41fb0a68c728f3.png](https://i-blog.csdnimg.cn/blog_migrate/f19695a8be0f36004b0e2b8faefeba85.jpeg)
尝试 获取锁。 如果是重入锁, 则计算 state的值。
如 图片中的138行中,当两个线程相等时。 则计算state的值。
大家都知道lock锁是可重入的, 那么怎么去计算重入锁的次数呢? 没错就是依赖state的值, lock 多少次, unlock 就要多少次
第三步, 重头戏 。 是如何构建 一个节点链表的呢? 比如线程c, 线程d进来争抢锁。 又是如何的存储的呢?
addWaiter(Node mode) : 创建节点 , 并加入队列。
![48f2db17c672555757fc15c8d15a3659.png](https://i-blog.csdnimg.cn/blog_migrate/7ce23253dbe53055eae95cb74a0e55fc.jpeg)
因为还初始化时, tail 会等于空, 所以会走 616 行代码
enq(Node node)
![3137044ff3e0f8909184cba9b76b4b11.png](https://i-blog.csdnimg.cn/blog_migrate/735a45ed7109213e654910c235e31993.png)
这是一个 循环, 会有两个过程循环的流程如下:
第一次循环, 初始化head,tail 节点, 并head==tail
![656eb647a2c7f390ef475a487c081a29.png](https://i-blog.csdnimg.cn/blog_migrate/30286f5151bcb3f66f76d027b4a2a37f.png)
第二次循环。设置新建节点的 per, 和next 节点,返回当线程node节点, 并跳出循环
![f1d16306fdb509db0beeb936b5356472.png](https://i-blog.csdnimg.cn/blog_migrate/f08f2b6201fbf2b3e93dfc970bcb517a.png)
第四步:
acquireQueued(addWaiter(Node.EXCLUSIVE), arg),
将每一个前置节点waitStatus= -1=Signal, 并阻塞每一个线程,不释放资源
![b6dd760bc5e86dbb02f86679ea1377d9.png](https://i-blog.csdnimg.cn/blog_migrate/7f8c7400e3ba0472f155ff4be2217eb1.jpeg)
final Node p = node.predecessor(); 获取每个节点的前置节点。
当由于state的 state不为0, 应为锁未释放。所以走到 869 行。
shouldParkAfterFailedAcquire(Node pred, Node node):是一个无限循环, 会将p节点(node 的前置节点 ) 的 wait 0->1.
![a37bc2704d0135bf6ab41f550f201962.png](https://i-blog.csdnimg.cn/blog_migrate/09cc6c9d32cc0fa6d217ed23f5081c18.png)
parkAndCheckInterrupt() 知道阻塞在这里。LockSupport.park(this); 让当前线程阻塞。 直至LockSupport.unpark 才会释放线程
LockSupport.park(this) 不释放线程资源。
![25416a953f9795c3e8b69291b952e86c.png](https://i-blog.csdnimg.cn/blog_migrate/017d98b09185c6259b4cd5a848fec64b.png)
解锁操作:
lock.unlock();
tryRelease方法,将status的减一, 如果为0 , 将当前线程置为空:setExclusiveOwnerThread(null);
unparkSuccessor(Node node),将当前的节点修改为0,waitStatus=0;
并释放挂起的线程 LockSupport.unpark(s.thread);
![f7ebf3fecfea1135a3f3e3c83e01e446.png](https://i-blog.csdnimg.cn/blog_migrate/2a20b3f0cbd061aafe50e67c2a6bd347.png)
线程停止阻塞后, 便会继续执行代码。 在第四步的无限循环中。因为state=0 , 且前节点时=head时, 会释放前节点。
![b7a9546eb13b4425dd0fcba23b95a193.png](https://i-blog.csdnimg.cn/blog_migrate/8a4767e73063901a54ffeb64f07a04ea.png)
则当前节点会置为新的head 节点。 原head 节点则会被 Gc 垃圾回收。
线程C 以此类推