加锁失败的时候如何借助AQS异步入队阻塞等待?

如果某线程加锁失败,他会把自己封装成一个Node,并会加入队列(双向链表),阻塞等待,别人释放锁。前面的内容接上一节内容。

1、AbstractQueuedSynchronizer类

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

尝试加锁失败,走以下逻辑

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

2、AbstractQueuedSynchronizer类

private Node addWaiter(Node mode) {
	// 将当前线程封装了一个Node
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    //一开始pred和tail肯定为null
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

分析:
1)将当前线程封装了一个Node

Node node = new Node(Thread.currentThread(), mode);

2)下面这行代码其实就是将上面构造的节点添加到双向队列中去。

enq(node);

点击enq(node),进入如下代码

3、AbstractQueuedSynchronizer类

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

1)一开始t肯定为null,第一轮循环如下分支

if (t == null) { // Must initialize
   if (compareAndSetHead(new Node()))
       tail = head;

这第一轮主要是构造了一个空Node
2)点击compareAndSetHead(new Node())进入如下代码
3)二轮之后的循环就会走这里

else {
    node.prev = t;
     if (compareAndSetTail(t, node)) {
         t.next = node;
         return t;
     }
 }

4)点击compareAndSetTail(t, node)
5)通过下面这两句就将空节点(其实就是头节点)和新构建的节点连接起来了。

node.prev = t;
。。。。。。。。
t.next = node;

4、AbstractQueuedSynchronizer类

private final boolean compareAndSetHead(Node update) {
      return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

分析:
1)这里的参数update为上面传入的空节点(new Node())。
2)headOffset -> 在AQS类里,head变量所在的位置。CAS判断,head变量是否为null,如果是null,就将head设置为空Node节点

5、AbstractQueuedSynchronizer类

private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

1)尝试比较tail变量是否为t,如果为t的话,那么tail指针就指向node。

6、AbstractQueuedSynchronizer类

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
        	// 获取到node的上一个节点
			// prev指针指向的节点
            final Node p = node.predecessor();
            // 这个地方,其实会再次调用tryAcquire方法尝试加锁
			// 如果加锁成功,其实是会将线程2对应的Node从队列中移除
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 如果说再次尝试加锁失败了
			// 那么此时会判断一下,是否需要将当前线程挂起,阻塞等待
			// 如果是需要的话,此时就会使用park操作挂起当前线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

分析:
1)这里的参数实际上为:线程2代表的Node,1。
2)点击setHead(node),剋看到代码如下:

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

这一段代码和下面的这一行,其实是:将线程2对应的节点清空,然后将头结点指过来。原来的头节点废掉。

setHead(node);

3)第一轮循环执行下面这行代码是返回false的。第二轮开始就返回true了。

shouldParkAfterFailedAcquire(p, node) 

4)点击如下代码进入

 parkAndCheckInterrupt())

7、AbstractQueuedSynchronizer类

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
    	// 将空Node的waitStatus设置为SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

分析:
1)参数pred为空node
2)刚开始ws=0,执行else片段。将空Node的waitStatus设置为SIGNAL

compareAndSetWaitStatus(pred, ws, Node.SIGNAL);

3)返回false到外层调用方法,第二次循环进来时会执行如下代码片段,并返回true

if (ws == Node.SIGNAL)   

8、AbstractQueuedSynchronizer类

private final boolean parkAndCheckInterrupt() {
	// LockSupport的park操作,就是将一个线程进行挂起,不让你动了
	// 必须得有另外一个线程来对当前线程执行unpark操作,唤醒挂起的线程
    LockSupport.park(this);
    //试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,
    //第二次再调用时中断状态已经被清除,将返回一个false。
    return Thread.interrupted();
}

9、AbstractQueuedSynchronizer类

上面的代码执行完后,又到了第1步。

public final void acquire(int arg) {
	// 先尝试加锁
	// 如果加锁失败,addWaiter()方法将自己挂到队列中去
	// 接着acquireQueued()方法负责park操作挂起当前线程,阻塞等待
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

10、一张图总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值