java.util.concurrent.CyclicBarrier provides a synchronization mechanism for multiple threads to wait for each other. It can be understood as an obstacle. All threads that reach the obstacle first will be in a waiting state. All threads can continue to execute until all threads reach the obstacle.
For example, the synchronization mode of CyclicBarrier is a bit like that when friends make an appointment to travel, they gather at the entrance of the scenic spot. The entrance of the scenic spot is a Barrier barrier, waiting for everyone to arrive before they enter the scenic spot together. After entering the scenic spot, we went to climb the mountain. Some people climbed fast and others climbed slowly. We made an appointment to gather at the top of the mountain, so the top of the mountain was another Barrier obstacle. We didn't go down the mountain until we all reached the top of the mountain.
The following is a diagram to illustrate this problem.
Each thread "waits for each other" at the CyclicBarrier barrier by calling await(). Once all threads reach the CyclicBarrier (all call the CyclicBarrier method), all threads will wake up again and continue to execute.
1. Create a CyclicBarrier barrier
When creating a CyclicBarrier, you need to specify how many threads need to be controlled for synchronization. For example, the following CyclicBarrier is set to control the synchronization of two threads.
CyclicBarrier barrier = new CyclicBarrier(2);
2. Wait at the CyclicBarrier obstacle
Call the await() method of CyclicBarrier to enter the wait state, which is usually called after the thread completes its stage task.
barrier.await();
CyclicBarrier also provides another method to specify the waiting timeout time. When the waiting time is greater than the timeout time, even if other threads do not call the await method, the thread will automatically wake up and continue to execute. (my friends have made an appointment to travel. I'll go first if you don't come after waiting for 10 minutes).
barrier.await(10, TimeUnit.SECONDS);
The waiting threads waits at theCyclicBarrieruntil either:
The condition that the thread waiting at the CyclicBarrier is released to continue execution (any of the following conditions can be met)
- The last arriving thread calls the await() method
- The thread was interrupted by another thread (another thread called its interrupt() method).
- Another waiting thread was interrupted
- Another waiting thread timed out while waiting at the CyclicBarrier.
- An external thread called CyclicBarrier.reset() to remove the barrier.
3. CyclicBarrier Action
CyclicBarrier Action is not easy to understand. It can be understood as the behavior of the barrier itself. The Action action is a thread. After all threads reach the obstacle, the thread will be executed.
Runnable barrierAction = Create thread; CyclicBarrier barrier = new CyclicBarrier(2, barrierAction);
If this code still fails to understand the role of CyclicBarrier Action, see the following example.
4. CyclicBarrier example
The following code demonstrates how to use CyclicBarrier for thread synchronization:
Runnable barrier1Action = new Runnable() { public void run() { System.out.println("Obstacle 1 collection succeeded, and everyone arrived at the gate of the scenic spot "); } }; Runnable barrier2Action = new Runnable() { public void run() { System.out.println("Obstacle 2 assembly succeeded and everyone reached the top of the mountain"); } }; //Barrier 1 entrance to scenic spot CyclicBarrier barrier1 = new CyclicBarrier(2, barrier1Action); //Obstacle 2 peak CyclicBarrier barrier2 = new CyclicBarrier(2, barrier2Action); //Tourism plan, stage goal 1: gather at the entrance of scenic spots CyclicBarrierRunnable barrierRunnable1 = new CyclicBarrierRunnable(barrier1, barrier2); //Tourism plan, stage goal 2: climb the mountain to the top of the mountain CyclicBarrierRunnable barrierRunnable2 = new CyclicBarrierRunnable(barrier1, barrier2); new Thread(barrierRunnable1).start(); //Visitor A, Thread-0 new Thread(barrierRunnable2).start(); //Visitor B, Thread-1
The following is a thread class CyclicBarrierRunnable. Starting one represents a visitor
public class CyclicBarrierRunnable implements Runnable{ CyclicBarrier barrier1 = null; //Obstacle 1 CyclicBarrier barrier2 = null; //Obstacle 2 public CyclicBarrierRunnable( CyclicBarrier barrier1,CyclicBarrier barrier2) { this.barrier1 = barrier1; this.barrier2 = barrier2; } public void run() { try { Thread.sleep(1000); //Here write the process code for going to the scenic spot System.out.println(Thread.currentThread().getName() + " Arrive at the entrance of the scenic spot"); this.barrier1.await(); Thread.sleep(1000); //Here write the process code of mountain climbing System.out.println(Thread.currentThread().getName() + " Climb to the top of the mountain"); this.barrier2.await(); System.out.println(Thread.currentThread().getName() + " Good play. Go down the mountain and go home!"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }
The following output is the execution print result of the above code. If you execute the above code several times, you will find that the arrival order of Thread-0 and Thread-1 at obstacle 1 and obstacle 2 is uncertain, but it always arrives first and then continues to execute.
Thread-0 Arrive at the entrance of the scenic spot Thread-1 Arrive at the entrance of the scenic spot Obstacle 1 collection succeeded, and everyone arrived at the gate of the scenic spot Thread-1 Climb to the top of the mountain Thread-0 Climb to the top of the mountain Obstacle 2 assembly succeeded and everyone reached the top of the mountain Thread-0 Good play. Go down the mountain and go home! Thread-1 Good play. Go down the mountain and go home!
Author: zimug
Link: https://juejin.cn/post/6945960442934067214