在尾部添加node , 将node双向关联 , 如果成功则直接返回 , 这里有一个问题 , 在设置队尾的时候 , 没有并发控制 , 有另一个线程也来设置 , 就只会有一个线程成功 , 没成功的线程或者队尾为空则执行enq方法 。
enq方法private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}这里看到如果tail是null,则cas设置head为一个新节点,也就是说第一个入队的节点head和tail是相同的 。如果队尾不为空 , 则用cas加自旋的方式放入队尾 。
Node对象node对象封装了状态和请求的线程以及前后节点的地址
static final class Node {//共享节点static final Node SHARED = new Node();//非共享节点static final Node EXCLUSIVE = null;//取消状态(因超时或中断)static final int CANCELLED =1;//等待唤醒static final int SIGNAL= -1;//等待条件static final int CONDITION = -2;//对应共享类型释放资源时 , 传播唤醒线程状态static final int PROPAGATE = -3;//当前状态volatile int waitStatus;//前一个节点volatile Node prev;//下一个节点volatile Node next;//请求的线程volatile Thread thread;Node nextWaiter;final boolean isShared() {return nextWaiter == SHARED;}//获取前一个节点 , 为空则抛空指针异常final Node predecessor() throws NullPointerException {Node p = prev;if (p == null)throw new NullPointerException();elsereturn p;}Node(Thread thread, Node mode) {// Used by addWaiterthis.nextWaiter = mode;this.thread = thread;}Node(Thread thread, int waitStatus) { // Used by Conditionthis.waitStatus = waitStatus;this.thread = thread;}}没有使用condition , node常用的状态有 0 新建状态和 -1 挂起状态 , waitStatus>0表示取消状态 , 而waitStatus<0表示有效状态 。
acquireQueued再看一下acquireQueued方法
final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {//获取前一个节点final Node p = node.predecessor();//如果前一个节点是head,并且能获取锁 , 则将当前节点设置为headif (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}//判断前面节点的状态 , 中断当前线程if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)//失败了设置成取消状态cancelAcquire(node);}}private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;//如果前一个节点已经是等待状态 , 可以安全parkif (ws == Node.SIGNAL)return true;//如何前一个节点是取消状态了 , 则一直往前取 , 去掉取消状态的节点 , 直到状态不为取消状态的节点if (ws > 0) {do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {//ws必须是0或-3才会走这里 , 第一个入队的节点是0状态 , cas设置成-1待唤醒状态 , 下一次循环就返回true了 , 将线程挂起compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}//中断当前线程private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();}这里的主要逻辑就是将新加入的节点设置为待唤醒状态 , 第一次抢到锁的线程不会进入队列 , 只有后续没有抢到的线程才进队列 , 进入队列的节点都进入中断状态 , 抢到锁的线程释放锁后唤醒head节点持有锁 , 锁被释放后会继续唤醒后面的节点代替之前的head成为新的head节点
release释放锁的过程 , 调用release方法
【java多线程编程的核心——AQS独占模式原理解析】public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;}private void unparkSuccessor(Node node) {int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);Node s = node.next;//清除取消状态的节点if (s == null || s.waitStatus > 0) {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);}调用LockSupport.unpark后 , 唤醒后一个中断的线程 , 队列剔除之前的head , 这样往复 , 释放锁后继续唤醒后面的线程 。
推荐阅读
- 2020年做自媒体还来得及吗?
- 柯尔鸭子的智商有多高 柯尔鸭为什么能当宠物
- 家里窗户全是瓢虫怎么办 窗户上很多瓢虫
- 老白茶煮多久好喝,生茶存放多久才好喝
- 冬季男人常吃三种菜 长寿又防癌
- 大猩猩体重最重有多少斤 猩猩最大的有多少斤
- 腿上有很多硬疙瘩痒 腿上起疙瘩痒还硬硬的
- 冬季心脑血管疾病高发 要多吃保护心脏的食物
- 冬天宝宝锻炼好处多 南北方冬季运动大不同
- 托运宠物收费标准 宠物店托运多少钱
