[Concurrent the Java] the AQS parse source code synchronization queue abstraction - lock acquisition process

To learn more about java concurrent knowledge, AbstractQueuedSynchronizer (AQS) is required to bring out the depth of learning, AQS can be said that throughout the entire JUC and contracting, for example ReentrantLock, CountDownLatch, CyclicBarrier and other concurrent classes are related to the AQS. Then realize the principle of AQS analysis.

Before starting the analysis, it is bound synchronization queue to find out first CLH

CLH synchronous queue

CLH spin lock: CLH (Craig, Landin, and Hagersten locks): is a spin lock, to ensure no hunger, providing fairness first-come, first-served basis. CLH spinlock is based on implicit list (there is no next node pointer) scalable, high-performance, and fair spin lock spin only application threads on the local variable that continuously polls the predecessor state, if We found precursors to release the lock on the end of a spin.

AQS synchronization queue in CLH: AQS synchronization queue is in CLH CLH spin lock has been optimized, it was modified in two major areas: the structure of nodes and node waits mechanism.

1.在结构上引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁、入队列、释放锁等实现都与头尾节点相关,并且每个节点都引入前驱节点和后后续节点的引用;

2.在等待机制上由原来的自旋改成阻塞唤醒。

CLH simple representation of the source code

*      +------+  prev +-----+       +-----+
* head |      | <---- |     | <---- |     |  tail
*      +------+       +-----+       +-----+

Node CLH is to achieve synchronous queue data structure, the calculation is to know the relevant field attributes of the class

AQS important inner class Node

static final class Node {
    // 共享模式
    static final Node SHARED = new Node();
    // 独占模式
    static final Node EXCLUSIVE = null;

    // 如果属性waitStatus == Node.CANCELLED,则表明该节点已经被取消
    static final int CANCELLED =  1;
    // 如果属性waitStatus == Node.SIGNAL,则表明后继节点等待被唤醒
    static final int SIGNAL    = -1;
    // 如果属性waitStatus == Node.CONDITION,则表明是Condition模式中的节点等待条件唤醒
    static final int CONDITION = -2; 
    // 如果属性waitStatus == Node.PROPAGATE,在共享模式下,传播式唤醒后继节点
    static final int PROPAGATE = -3; 
    // 用于标记当前节点的状态,取值为1,-1,-2,-3,分别对应以上4个取值
    volatile int waitStatus;
    // 前驱节点
    volatile Node prev;
    // 后继节点
    volatile Node next;
    // 当前节点对应的线程,阻塞与唤醒的线程
    volatile Thread thread;
    // 使用Condtion时(共享模式下)的后继节点,在独占模式中不会使用
    Node nextWaiter;
    final boolean isShared() {
            return nextWaiter == SHARED;
    }
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {    // Used to establish initial head or SHARED marker
    }

    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

The following began to focus on the important methods of the analysis explained AQS

Acquire a lock

1.acquire began to acquire a lock

public final void acquire(int arg) {
    //如果tryAcquire返回true,即获取到锁就停止执行,否则继续向下执行向同步队列尾部添加节点
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire method for acquiring a lock, there is no default AQS achieve specific logic, custom implemented by subclasses.

If it returns true then the acquired lock, otherwise the current thread needs to be packaged as Node node is added to the end of the queue synchronization.

2. The current node queues

The currently executing thread package is added to the Node node and the tail

private Node addWaiter(Node mode) {// 首先尝试快速添加到队尾,失败再正常执行添加到队尾
    Node node = new Node(Thread.currentThread(), mode);
    // 快速方式尝试直接添加到队尾
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 如果快速添加到队尾失败则执行enq(node)添加到队尾
    enq(node);
    return node;
}

The method loops through enq added to the tail

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;
            }
        }
    }
}

addWaiter (Node mode) after the implementation of the method is performed next acquireQueued method returns whether the thread needs to be interrupted, which is constantly circulating acquiring the lock, if the first node is the first node, and then attempt to acquire the lock, the lock acquisition success returns whether the interrupt flag, if acquiring the lock fails, it is determined whether blocking and blocking threads

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        // 标记是否需要被中断
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            // 如果前驱节点是头节点,并且获取锁成功,则返回
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 判断获取锁失败后是否需要阻塞当前线程,如果阻塞线程后再判断是否需要被中断线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

Whether shouldParkAfterFailedAcquire (p, node) method to determine the current thread needs to be blocked

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    // 如果ws == Node.SIGNAL,则说明当前线程已经准备好被唤醒,因此现在可以被阻塞,之后等待被唤醒
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        // 如果ws > 0,说明当前节点已经被取消,因此循环剔除ws>0的前驱节点
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //如果ws<=0,则将标志位设置为Node.SIGNAL,当还不可被阻塞,需要的等待下次执行shouldParkAfterFailedAcquire判断是否需要阻塞
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

Returns true if shouldParkAfterFailedAcquire (p, node) method, the need blocks the current thread, parkAndCheckInterrupt method blocks the thread is executed, and the process of return is blocked thread is interrupted

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this); // 阻塞线程,等待unpark()或interrupt()唤醒自己
    // 线程被唤醒后查看是否被中断过。
    return Thread.interrupted();
}

Then back to lock acquisition method acquire method returns true if acquireQueued (final Node node, int arg), that is, the process of blocking thread is interrupted, the interrupt thread selfInterrupt operation is performed ()

public final void acquire(int arg) {
    //如果tryAcquire返回true,即获取到锁就停止执行,否则继续向下执行向同步队列尾部添加节点,然后判断是否被中断过,是则执行中断
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

Interrupts the current thread

static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

summary

AQS acquire a lock process:

1. Perform tryAcquire method to acquire the lock, the lock if the acquisition is successful direct return, otherwise step 2

2. The method of execution of the current thread package addWaiter bits and added to the sync node Node tail of the queue, step 3

3. Do acquireQueued loop attempts to acquire a lock, that if acquiring the lock is successful, the judge returns to the interrupt flag, if it fails to acquire a lock judgment call whether shouldParkAfterFailedAcquire method needs to block the current thread, if necessary block the thread parkAndCheckInterrupt blocking the calling thread, and being and then wake up again to determine whether blocking the process of being interrupted.

4. If acquireQueued returns true, the explanation was interrupted thread is blocked in the process is executed selfInterrupt interrupt thread

Well, that's AQS lock acquisition process, analysis of the lock release will continue in subsequent output.

Guess you like

Origin www.cnblogs.com/zyg-coding/p/12045194.html
Recommended