Summary
CountDownLatch: Allows one or more threads to wait for other threads to complete operations
Cyclic Barrier: When a group of threads reaches the synchronization point, they are blocked until the last thread reaches the synchronization point, and all the intercepted threads will not continue to run.
Semaphore: Semaphore, a semaphore used to control the number of threads accessing specific resources at the same time, which ensures the rational use of common resources by coordinating threads
Exchanger: Used for data exchange between threads
CountDownLatch
CountDownLatch allows one or more threads to wait for other threads to execute. A typical application scenario for CountDownLatch is that one task wants to go down, but it has to wait until the other tasks are finished before it can continue.
Constructor:
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
The constructor of CountDownLatch can pass in an int parameter N, indicating that it needs to wait for N points to complete. When CountDownLatch's countDown() method is called, N is reduced by 1. CountDownLatch's await method blocks the current thread until N becomes zero.
CountDownLatch instance:
/**
* Ten people were divided into two groups, five in each group. When the starting gun was fired, the competition began.
* @author Administrator
*
*/
public class CountDownLatchDemo {
private static final int GROUP_SIZE=5;
private static final Random RANDOM = new Random();
private static void processOneGroup(final String groupName){
//Wait so the players are ready.
final CountDownLatch preCountDownLatch = new CountDownLatch(GROUP_SIZE);
//Waiting for the game to begin
final CountDownLatch startCountDownLatch = new CountDownLatch(1);
//Waiting for the end of the game
final CountDownLatch endCountDownLatch = new CountDownLatch(GROUP_SIZE);
System.out.println(groupName+",The competition begins:");
for (int i = 0; i < GROUP_SIZE; i++) {
new Thread(String.valueOf(i)){
public void run() {
preCountDownLatch.countDown();//Ready
System.out.println("The first"+GROUP_SIZE+"Group No."+this.getName()+"No. 1 player, ready!~");
try {
//Waiting for the referee to issue a start order
startCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
//Run a random time to indicate the actual race time of the competitor
Thread.sleep(Math.abs(RANDOM.nextInt())%1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Finish the game
endCountDownLatch.countDown();
};
}.start();
}
try {
preCountDownLatch.await();//Waiting for all the players to be ready
} catch (InterruptedException e) {
e.printStackTrace();
}
startCountDownLatch.countDown();//Start the game
try {
endCountDownLatch.await();//Waiting for all the players to finish the race
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(groupName+" The game is over!");
}
public static void main(String[] args) {
processOneGroup("Group 1");
processOneGroup("Group 2");
}
}
Output results:
Group 1, start the game:
Group 5, No. 0, is ready! ~
Group 5, No. 2, is ready! ~
Group 5, No. 1, is ready! ~
Group 5, No. 3, is ready! ~
Group 5, No. 4, is ready! ~
Group 1 is over!
Group 2, start the game:
Group 5, No. 0, is ready! ~
Group 5, No. 1, is ready! ~
Group 5, No. 2, is ready! ~
Group 5, No. 3, is ready! ~
Group 5, No. 4, is ready! ~
Group 2 is over!
The countDown method is implemented as follows:
/**
* Reduce the count of latches and release all waiting threads if the count reaches zero.
* If the current count is greater than zero, it decreases. If the new count is zero, all waiting threads are re-enabled for thread scheduling purposes. If the current count equals zero, nothing happens.
*/
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
// Decreasing count; until signal is converted to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
/**
* Ensure release dissemination, even if there are other ongoing acquisitions/releases. If it needs a signal, it tries to unhead the processor in the usual way. But if not, the status is set to PROPAGATE to ensure that the propagation continues after release. In addition, we have to loop in order to add a new node when we do so. In addition, unlike other uses of unparkSuccessor, we need to know if CAS failed to reset the state, if it did.
*/
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
The difference between CountDownLatch and Thread.join();
The thread.join() method must wait for threads to execute before the current thread can continue to execute, while CountDownLatch provides more flexible control through the counter, as long as it detects that the counter is 0, the current thread can execute downward regardless of whether the corresponding thread has finished executing or not.