It’s hard to explain AQS, but the interviewer asked me to talk about the application scenarios, so I had to talk about CountDownLatch again~~~

Article Directory



Preface

The bloggers have been wandering in the Niuke Mianjing area all the year round, summarizing the high-frequency exam questions from big companies such as Byte, Ali, Baidu, Tencent, Meituan, etc., and will gradually share them with everyone afterwards. I look forward to your attention and praise!

Insert picture description here



CountdownLatch source code explanation

The main principle of CountdownLatch relies on AQS. Those who don’t know AQS can read this blog post to understand, it’s easy to get started with AQS.

AQS quick start

Not much to say, let's talk, oh no, let's talk! ! !


CountDownLatch mainly has two methods:①: await(), ②: countDown();

Then I don’t want to buy a pass, I just said:
the thread that calls the await() method will be blocked, until the counter is reduced to 0, can continue to execute;
countDown(): decrement the counter by one,
believe it Friends will understand after reading my source code explanation.

But let’s start with the CountDownLatch constructor



CountDownLatch constructor

CountDownLatch countDownLatch = new CountDownLatch(2);

Here you can see that the constructor of CountDownLatch is actually a Sync of new, and the int type value we passed in is also used as a Sync parameter

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

Let's click in again, we can see that Sync inherits AbstractQueuedSynchronizer or AQS

And the parameterized structure of Sync calls the setState(count); method

Insert picture description here

Look at the setState method again,In fact, to put it plainly is to use the int value passed in by our CountDownLatch as the synchronization state of AQS.

Insert picture description here
At this point, it is strongly recommended that you start with AQS first, and then look at the tools under this concurrent package. It is really not too simple! ! !
AQS quick start



countDown() icon, source code explanation

Let's take a look at the countDown method

public void countDown() {
    sync.releaseShared(1);
}

Here the releaseShared method of sync is called, and arg = 1 is passed in

And we can see that releaseShared involves two methods tryReleaseShared(1) and doReleaseShared()

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

First look at tryReleaseShared(1)

Insert picture description here

I believe that this source code can be understood by a small partner at a glance, so I will briefly summarize it!

  • Call the getState() method to get the synchronization state. If the synchronization state is 0, it returns false. Combined with the code below, it means that the synchronization state cannot be reduced.
  • If it is not 0, it can also be said to be greater than 0, then use an int variable to record the value of the synchronization state minus one
  • Finally, the synchronization CAS sets the synchronization status to the value minus one. If the setting fails, it spins to retry. If it succeeds, it depends on whether the value minus one is 0.
Those who don’t know CAS can also check out my blog, which has a high number of hits: When I was asked about CAS principles in the interview, my face turned green when I touched the blind spot of knowledge!
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;
    }
}

Combined with the previous source code, if the synchronization state after subtracting one is 0, then the doReleaseShared() method will be called

Insert picture description here
private void doReleaseShared() {
    for (;;) {
      //获取头结点
        Node h = head;
        if (h != null && h != tail) {
          //获取head结点的等待状态
            int ws = h.waitStatus;
          //如果head结点的状态为SIGNAL, 表明后面有人在排队
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                  //直到CAS成功
                    continue;            
                //再去唤醒后继结点;在独占锁的时候有说明,这里就不多说了; 
              unparkSuccessor(h);
            }
          //如果head结点的状态为0, 表明此时后面没人在排队, 就只是将head状态修改为PROPAGATE
     	else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                 //直到CAS成功
                 continue;                
        }
        if (h == head)                   
            break;
    }
}

Maybe the friends didn’t understand the source code, so let me summarize it.

  • It will be called only when the synchronization status is 0, doReleaseShared() method
  • If the synchronization status is 0, the lock is not occupied by threads
  • Then it involves the doReleaseShared() method to see if there is a thread queued behind the thread
  • If there is a thread queued, upark wakes it up, and whether there is a node or not, the status of the current node must be updated



await() icon, source code explanation

The acquireSharedInterruptibly(1) method of sync called by the await() method

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

Click in and see

The first if has nothing to say, if the thread is interrupted, an exception will be thrown

Mainly the second if, involving tryAcquireShared(1), doAcquireSharedInterruptibly(1) methods

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

tryAcquireShared(1) has nothing to do with the incoming parameters, it is to see whether the current synchronization state is 0, if it is 0, it returns 1, and if it is not 0, it returns -1

Then a friend asked, why did you return like this? ? ?

Don’t worry, let’s look at the second method

protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}

doAcquireSharedInterruptibly

You can ignore everything else, the key lies in this for (;;) loop.
We can see that if you want to exit the for loop, you must meet(P == head) && (r >= 0), Which isThe current node is equal to the head node, and the synchronization state is 0

tryAcquireShared we introduced above
Tips: It is recommended to understand AQS: AQS quick start
private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

Ok, I believe that the friends who can see here may be a little less able to understand, then I will summarize it for you.
It is not easy to create, and I hope my friends can support a lot with one-click and three consecutive games! ! ! 😁



to sum up

As I said at the beginning,
the thread calling the await() method will be blocked until the counter is reduced to 0 before it can continue to execute;
countDown(): Decrement the counter by one.

Combined with the above source code explanation,Let's talk about await() first

  • If the counter is not 0, then tryAcquireShared will certainly return to 1, r >= 0would not be satisfied, then you can not quit, it would have been carried out for the cycle that is to play the role of blocking

Let's talk about countDown()

Each call to the countDown() method will use CAS to decrement the counter by one

It will be called only when the synchronization status is 0, doReleaseShared() method

  • If the synchronization status is 0, the lock is not occupied by threads

Then it involves the doReleaseShared() method to see if there is a thread queued behind the thread

  • If there is a thread queued, upark wakes it up, and whether there is a node or not, the status of the current node must be updated



CSDN exclusive benefits are coming! ! !


Recently, CSDN has an exclusive production activity, which is the following "Java Full Stack Knowledge Graph". The route planning is very detailed, and the size is that 870mm x 560mmfriends can follow the above process for systematic learning, don't just find the book yourself. To learn, we must study systematically and regularly, so that the foundation is the most solid. In our industry, "The foundation is not strong, the ground is shaking" is especially obvious.

Finally, if you are interested, you can buy it as appropriate to pave the way for your future! ! !


Insert picture description here

At last

I am a master of CRUD sketching , a Pippi shrimp enthusiast who loves to share knowledge, and I will continue to update blog posts that are beneficial to everyone in the coming days. I look forward to your attention! ! !

It’s not easy to create. If this blog post is helpful to you, I hope you guys canOne-click three consecutive!, Thanks for your support, see you next time~~~

Share outline

Dachang Interview Questions Column


Java From Entry to Grave Learning Route Catalog Index


Open Source Crawler Example Tutorial Catalog Index

For more exciting content to share, please click Hello World (●'◡'●)


Insert picture description here