On the learning of java Concurrent Programming

Keywords: Java less

I talked about AQS before. Source code of exclusive lock , here, let's talk about the implementation of another lock, shared lock, to CountDownLatch For example.

Source code analysis

Construction method

The Sync method is an internal method, just like ReentrantLock before. The constructor needs to pass in an integer no less than 0 to assign to state. When other threads call the countDown method, the value of state is reduced by one. When it is reduced to 0, other calls to the await method will be awakened. The await method can be called by multiple threads. When it is called, it enters the blocking state until the state is 0. Later, we will focus on countDown and await methods.

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
Sync(int count) {
    setState(count);
}

await

Let's first look at the blocking method. At this time, we need to wait for the state to be 0 before being awakened.

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
    if (Thread.interrupted())//Interrupt condition, throw exception
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);//When state is not 0
}
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;//Current status is 0, return 1
}
private void doAcquireSharedInterruptibly(int arg)//Acquire the shared lock and can be interrupted
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);//This is different from the previous Node.SHARED. It is added to the queue and is not explained.
    boolean failed = true;
    try {
        for (;;) {//spin
            final Node p = node.predecessor();//Get front node
            if (p == head) {
                int r = tryAcquireShared(arg);//Attempt to acquire lock
                if (r >= 0) {//When state is 0
                    setHeadAndPropagate(node, r);//This method is described below
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())//This part is the same as before, hang
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

countDown

public void countDown() {
    sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();//true, wake up
        return true;
    }
    return false;
}
//Using spin to make status-1
protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    for (;;) {
        int c = getState();
        if (c == 0)
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {//There is a blocking queue and the head node is not the tail node
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))//Set the waitStatus of the header node to 0 through cas
                    continue;            // loop to recheck cases failed to reset
                unparkSuccessor(h);//Wake up next node, previous content
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;//
    }
}

When just wait ing, if the lock is not acquired in doacquireshared interruptible, it will be suspended. Now that the status is 0, wake him up and continue spinning. See the following code for the number of times

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    setHead(node);//Current node set as head node
    
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;//Get next node
        if (s == null || s.isShared())
            doReleaseShared();//Wake up next node
    }
}

Posted by benyboi on Wed, 16 Oct 2019 14:30:57 -0700