This is the sixth day of my participation in Gwen Challenge
1. CountDownLatch profile
In the previous article, we introduced the CyclicBarrier class, which allows you to perform batch summaries. This time, CountDownLatch is a “countdown” function.
2. Simple application of CountDownLatch
The CountDownLatch source code is concise and provides two typical methods for implementing the “countdown function.” CountDownLatch specifies the number of latches in the constructor. When the number of latches is zero, all threads in the wait state are awakened. It provides the following key approaches:
countDown()
: by callingcountDown
Method to realize latch lock- 1
The effect.await()
: makes the current thread wait.getCount()
: Gets the number of latches.
Let’s take a look at a sample code:
public class MyTest {
static CountDownLatch countDownLatch;
public static void main(String[]args){
countDownLatch = new CountDownLatch(3);
for(int i=0; i<3; i++){new Thread(new Runnable() {
@Override
public void run(a) {
System.out.println(Thread.currentThread().getName() + "-- to the door");
System.out.println(Thread.currentThread().getName() + "-- boarded");
countDownLatch.countDown();
}
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-- Boarding complete."); }}Copy the code
In the example code, the constructor creates a CountDownLatch method with 3 latches, creates three threads, calls the countDown() method inside each thread once, and finally calls the await method in the main thread to block. Finish the output. Execution Result:
Thread-0-- Reaching the door Thread-1-- Reaching the door Thread-0-- Boarded Thread-1-- Boarded Thread-2-- Reaching the door Thread-2-- Boarding is completeCopy the code
CountDownLatch has the following features:
- Thread execution
countDown
Method can continue to execute subsequent code, the thread does not block waiting; latch
The number of0
Will immediately wake upawait
Waiting threads;
3. CountDownLatch source code analysis
Considering the description of the CountDownLatch function above, if we were to implement such a function, our first thought would be to maintain a Count Count variable in the utility class, and then maintain the judgment on that variable. So let’s look at how CountDownLatch is implemented.
public class CountDownLatch {
/** * Synchronization control For CountDownLatch. * Uses AQS state to represent count. */
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount(a) {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0)?1 : -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 final Sync sync;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/** * Makes the current thread wait until the number of locks is 0, or the current thread interrupt * returns */ immediately if the current latch number is 0
public void await(a) throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/** * Reduce the number of latch locks and release all waiting threads when the number of LATCH locks is zero. * /
public void countDown(a) {
sync.releaseShared(1);
}
public long getCount(a) {
return sync.getCount();
}
public String toString(a) {
return super.toString() + "[Count = " + sync.getCount() + "]"; }}Copy the code
First, in terms of constructors, CountDownLatch provides the argument constructor:
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Copy the code
The Sync class here is an internal class that integrates the AQS implementation within CountDownLatch.
Sync(int count) {
setState(count);
}
Copy the code
In the Sync constructor, the AQS setState method is called to change the state value to the count value.
As described above, the CountDownLatch and await methods are the most used for the CountDownLatch class.
3.1 countDown () method
public void countDown(a) {
sync.releaseShared(1);
}
/** The method defined in the AQS class */
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
Copy the code
Inside the countDown method is a call to Sync’s releaseShared method to try to release the public lock state.
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; }}Copy the code
If the state value is 0, false is returned. If the CAS operation succeeds, nexTC == 0 is returned. When true is finally returned by consulting (state == 0), the doReleaseShared() method in releaseShared is executed, The blocked threads are awakened to start executing by the unparksucceeded () method inside the doReleaseShared() method.
3.2 await () method
public void await(a) throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/***AQS */
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0)?1 : -1;
}
Copy the code
Here call acquireSharedInterruptibly lock () method of the application, if the lock is held the state! = 0, then through doAcquireSharedInterruptibly () to lock application.
4. To summarize
CountDownLatch is commonly used in the following scenarios:
- Ensure that a computation does not proceed until all the resources it needs have been initialized.
- Ensure that a service is started only after all other services on which it depends have been started.
- Wait until the owners of an operation are ready to proceed.