Welcome to King of Concurrency. This is the 21st article in the series, and the eighth in Platinum.

In the last article, we introduced the use of CountDownLatch. CountDownLatch is a great choice for coordinating the start and end of multiple threads. CyclicBarrier, which I’ll show you in this article, is more interesting because it has both similarities and significant differences in capabilities with CountDownLatch, and it’s worth taking a look at. This article will walk you through the problem in terms of the scenario and then understand the solution provided by CyclicBarrier.

First, CyclicBarrier experience

1. Love in the canyon forest

In the valley of rivers and lakes, there are not only life and death and the light of the sword, but also a wonderful love story.

Canyon warlord armor once saved big Joe in a critical moment, this a hero to save the United States let them wipe the spark of love, something nothing two people in the valley in each corner tryst. Among them, the valley forest is the place where they often go, the first to wait for the other, both of them arrived, then play together.

There are two important points here. One is that they wait for each other, and the other is that they play when they are all together. Now, let’s think about what you would do if you simulated this scenario in code. Some of you might say that two people (threads) waiting is easy to handle. But what about three people?

So, the scenario problem can be summarized as: multiple threads wait for each other, and then perform a specific action.

Next, we will use CyclicBarrier to simulate and solve the problem in this scenario, and intuitively experience the usage of CyclicBarrier.

In this code, we define a rendezvous place, and big Joe and Kai, two heroes, appointplace. After they all arrive at the rendezvous, we output a sentence containing three roses 🌹🌹🌹 to confirm and send them our blessing.

 private static String appointmentPlace = "Canyon Forest";

 public static void main(String[] args) {
   CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> print("🌹🌹🌹 Arrive at the rendezvous: Big Joe and Kai both come to 👉"+ appointmentPlace)); Thread big Joe = newThread("Big Joe", () -> {
     say("Gai, where are you...");
     try {
       cyclicBarrier.await(); // Arrive at the tryst location
       say("Kai, there you are...");
     } catch(Exception e) { e.printStackTrace(); }}); Thread armoured = newThread ("Armor", () - > {try {
       Thread.sleep(500); // In the field
       say("I'll be there in a minute!");
       cyclicBarrier.await(); // Arrive at the tryst location
       say("I'm sorry, Joe. I just ran into The King of Lanling. Are you all right? !");
     } catch(Exception e) { e.printStackTrace(); }}); Big Joe. The start (); Armoured. Start (); }Copy the code

The output is as follows:

Big Joe: Kai, where are you... Kai: I play wild, right away! 🌹🌹🌹 Arriving at the rendezvous: Big Joe and Kai have come to 👉 Canyon forest Kai: Sorry, Joe, I just met the king of Lanling in the field. Are you ok? ! Big Joe: Kai, there you are... Process finished with exit code 0Copy the code

There is no need to go into the details of the code for the moment, and the internal details of CyclicBarrier will be explained later in this article. First, feel its basic usage.

As you can see from the results, CyclicBarrier, like CountDownLatch, can coordinate the end-of-execution actions of multiple threads, executing specific actions after they have all finished. In this sense, this is where CyclicBarrier is similar to CountDownLatch. The scene that follows, however, shows one obvious difference.

2. Tryst by the river

In the above scene, Kai has mentioned that he met the King of Lanling while playing wild. And in kai and big Joe in the date, lanling king actually ran into them again, it is a narrow road. So, in the king of Lanling, kai and big Joe had to move position, they also agreed to wait for each other after the new agreed location. (armor has always thought that the king of Lanling also like big Joe, and he stole love, in fact, the king of Lanling only care about armor hit it wild, his heart only wild, no interest in any woman).

At this point, if you continue to simulate this scenario with your code, CountDownLatch will not be able to do anything about it, because CountDownLatch is used once and cannot be reused. That’s when you see the magic of CyclicBarrier: it can be reused. As if, you might already have a general idea of why it’s called Cyclic.

Next, let’s go through a piece of code to simulate big Joe and Kai’s second tryst. In the code, we still define the rendezvous location, big Joe and Kai. But unlike before, we also added a spoiler, the Lanling King, and changed the location of the tryst.

private static String appointmentPlace = "Canyon Forest";

public static void main(String[] args) {
  CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> System.out.println("🌹🌹🌹 Arrive at the rendezvous: Big Joe and Kai both come to 👉"+ appointmentPlace)); Thread big Joe = newThread("Big Joe", () -> {
    say("Gai, where are you...");
    try {
      cyclicBarrier.await();
      say("Kai, there you are...");
      Thread.sleep(2600); // On a date...
      say("Okay, be careful!);
      cyclicBarrier.await(); // Note that this is the second call of await
      Thread.sleep(100);
      say("I will!);
    } catch(Exception e) { e.printStackTrace(); }}); Thread armoured = newThread ("Armor", () - > {try {
      Thread.sleep(500); // In the field
      say("I'll be there in a minute!");
      cyclicBarrier.await(); // Arrive at the tryst location
      say("I'm sorry, Joe. I just ran into The King of Lanling. Are you all right? !");
      Thread.sleep(1500); // In a tryst...

      note("Tryst... \n");

      Thread.sleep(1000); // In a tryst...
      say("Damn the King of Lanling! You go on, Joe. I'll see you at the brook."); // Suddenly Kai saw The King of Lanling
      appointmentPlace = "The river."; Kai changed the location to a river

      Thread.sleep(1500); // In the battle with the King of Lanling...
      note("︎\uD83D\uDDE1\uD83D\uDD2A The final battle between Kai and the Lanling King begins. Finally kai kills the Lanling King and heads to the river... \n");
      cyclicBarrier.await(); // After killing The Lanling Queen, kai went to the river!! Note that this is the second call to await

      say("Joe, I have solved the king of Lanling, you see the night is beautiful, I accompany you to watch the stars until dawn...");
    } catch(Exception ignored) {} }); Thread = newThread("The King of Lanling", () - > {try {
      Thread.sleep(2500);
      note("The King of Lanling appears...");
      say("I will not rest until I have killed him!");

      say("Kai, here you are with Big Joe! \ uD83D \ uDDE1 ️ \ uD83D \ uDDE1 ️");
    } catch(Exception ignored) {} }); Lanling king. The start (); Big Joe. The start (); Armoured. Start (); }Copy the code

The following output is displayed. Gai canyon forest good thing was lanling king after the stir, gai let big Joe go first, and agreed to meet on the river. Kai then beheads the king of Lanling (a poor steel straight man) and heads to the river to complete his second tryst with Big Joe.

Big Joe: Kai, where are you... Kai: I play wild, right away! 🌹🌹🌹 Arriving at the rendezvous: Big Joe and Kai have come to 👉 Canyon forest Kai: Sorry, Joe, I just met the king of Lanling in the field. Are you ok? ! Big Joe: Kai, there you are... Tryst in... King Lanling appears... King Lanling: Kai beat my wild, I will not stop until he is killed! King Lanling: Kai, so you and big Joe are here! 🗡️🗡️ armored: The bloody King of Lanling! Go ahead, Joe. I'll see you at the creek! Big Joe: Okay, be careful! ︎🗡🔪 Kai begins a decisive battle with the Lanling King. Finally Kai kills the Lanling King and heads to the river... Kai: Joe, I have taken care of the king of Lanling. Look how beautiful the night is tonight. I will accompany you to see the stars until the morning. Big Joe: Great! Process finished with exit code0
Copy the code

Again, you’ll ignore the details of the code for now, but notice that kai and Big Joe have two calls to await(). It’s normal to be surprised at how amazing it is before you understand how it works.

How is CyclicBarrier implemented

CyclicBarrier is a thread synchronization tool provided in Java. It is similar to CountDownLatch, but not identical to CountDownLatch. The core difference is that CyclicBarrier can be used in cycles, which is reflected in the name.

Next, let’s analyze the specific source code implementation.

1. Core data structure

  • private final ReentrantLock lock = new ReentrantLock(): Only one lock to enter the barrier;
  • private final Condition trip = lock.newCondition(): used with lock above;
  • private final int parties: The number of participants, the above examples in this paper are only Kai and Big Qiao, so the number is 2;
  • private final Runnable barrierCommand: The specific code to run at the end of the round. This article uses it in the examples above, which you can scroll up to see;
  • private Generation generation = new Generation(): Generation of the current barrier. For example, in the above two scenarios, generation is different, in kai and big Joe will be tryst site changed to the river, will generate new generation;
  • private int count: Number of waiting parties. In each generation, the count drops from the original number of participants (i.e., parties) to 0, at which point the generation ends, and when a new generation or the generation is broken (i.e., broken), the value of the count is restored to the value of the parties.

2. Core structure

  • public CyclicBarrier(int parties): Number of designated participants;
  • public CyclicBarrier(int parties, Runnable barrierAction): Specifies the number of participants and specifies the code to run at the end of the generation.

3. Core methods

  • public int await(): If the current thread is not the first to reach the barrier, it will wait until all other threads have arrived, unless this happensBe interrupted,The barrier was removed,The barrier is resetWait for a circumstance;
  • public int await(long timeout, TimeUnit unit): similar to await(), but with a time limit;
  • public boolean isBroken(): Whether the current barrier is removed;
  • public void reset(): Resets the current barrier. Barriers are removed before new ones are put up;
  • public int getNumberWaiting(): Indicates the number of waiting threads.

The core of the CyclicBarrier methods is dowait(), which is called internally by both await() methods. So, if you understand dowait(), you basically understand the key to implementing CyclicBarrier.

The dowait() method is a bit longer and requires a bit more patience, and I’ve already commented some of it. Of course, if you want to see the source code, it is recommended to look at it directly from the JDK. The source code here is just to help you understand the context.

private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock(); // Note that the lock must be added
    try {
        final Generation g = generation;

        if (g.broken) // Throw an exception if the current barrier is removed
            throw new BrokenBarrierException();

        if (Thread.interrupted()) { 
            breakBarrier(); // If the current thread is interrupted, remove the barrier and throw an exception
            throw new InterruptedException();
        }

        int index = --count; // When the thread calls await, count is reduced by 1
        if (index == 0) { Tripped // If count is 0, the next thing to do is to try to end the barrier and open new ones
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if(command ! =null)
                    command.run();
                ranAction = true;
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
            try {
                if(! timed) trip.await();else if (nanos > 0 L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if(g == generation && ! g.broken) { breakBarrier();throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.Thread.currentThread().interrupt(); }}if (g.broken)
                throw new BrokenBarrierException();

            if(g ! = generation)return index;

            if (timed && nanos <= 0 L) {
                breakBarrier();
                throw newTimeoutException(); }}}finally{ lock.unlock(); }}Copy the code

The core data structures, constructs, and methods for CyclicBarrier are all up there, and they are important. But, more importantly, to understand the idea of CyclicBarrier, this is a picture worth keeping in your collection. If you understand this picture, you understand CyclicBarrier.

At this time, looking back at the two scenes in the first section from this picture, Kai and Big Joe have a tryst in the canyon forest and the river successively. So, if I had a picture, it would look something like this:

CyclicBarrier is different from CountDownLatch

The two core differences have been mentioned in the previous two sections:

  • While CountDownLatch is one-time, CyclicBarrier can be set up multiple times for reuse.
  • Each subthread in the CountDownLatch cannot wait for another thread and can only complete its own task; Each thread in CyclicBarrier can wait for another thread.

In addition, there are some other differences between the two, which are summarized in the following table:

CyclicBarrier CountDownLatch
CyclicBarrier is reusable in that threads wait for all threads to complete their tasks. At that point, the barrier will be removed and selected for certain actions. CountDownLatch is one-time, with different threads working on the same counter until the counter reaches 0.
CyclicBarrier is oriented towards the number of threads CountDownLatch is for the number of tasks
When using CyclicBarrier, you must specify in the construct the number of threads participating in the collaboration, and these threads must call the await() method When using CountDownLatch, you must specify the number of tasks, and it doesn’t matter which threads do the tasks
CyclicBarrier can be reused after all threads are released CountDownLatch can no longer be used when the counter is 0
In CyclicBarrier, if one thread experiences interrupts, timeouts, etc., the await thread will have problems In CountDownLatch, if one thread has a problem, the other threads are not affected

summary

That’s all about CyclicBarrier. When learning CyclicBarrier, focus on understanding the problem scenario it is trying to solve, and how it is different from CountDownLatch, and then look at the source code, which is why we don’t just put the source code in the first place, but instead tell a story around it, even if it is a very bad story. Of course, if this story makes you remember that, it’s worth it.

At the end of the text, congratulations on your another star ✨

The teacher’s trial

  • Write code to experience CyclicBarrier usage.

Further reading and references

  • “King concurrent course” outline and update progress overview

About the author

Pay attention to [technology 8:30], get the article updates in time. Pass on quality technical articles, record the coming-of-age stories of ordinary people, and occasionally talk about life and ideals. 8:30 in the morning push author quality original, 20:30 in the evening push industry depth good article.

If this article is helpful to you, welcome to like, follow, supervise, we together from bronze to king.