JUC contains some common synchronization tool classes. Today, we will discuss in detail how to use CountDownLatch, CyclicBarrier, Semaphore and the differences between them.

A, CountDownLatch

Take a look at the official introduction of CountDownLatch source code.

That is, it is a synchronization helper that allows one or more threads to wait until a set of operations performed on other threads are complete.

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

Its constructor passes in a count value to count.

There are two common methods:

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

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

When a thread calls the await method, it blocks the current thread. Every time a thread calls the countDown method once, the count is reduced by one. A blocked thread will continue running only when count equals zero.

Now imagine a scenario, a company project, there is an urgent bug online, customer complaints, the leader anxious to find someone to quickly solve the bug.

So, a person to solve the affirmation speed is slow, hence call zhang SAN and Li Si, work out the division of labor together. Finally, when both of them have done what they need to do, the leader can reply to the customer, and the customer will be relieved (no way, customer is god).

Therefore, we can design a Worker class to simulate the process of fixing the bug by a single person. The main thread is the leader, waiting for the completion of all Worker tasks before the main thread can continue to move down.

public class CountDownTest { static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(2); Worker w1 = new Worker(latch, 2000); Worker w2 = new Worker(latch); w1.start(); w2.start(); long startTime = System.currentTimeMillis(); latch.await(); System.out.println(" + (system.currentTimemillis () -startTime)); } static class Worker extends Thread{ String name; int workTime; CountDownLatch latch; public Worker(String name, int workTime, CountDownLatch latch) { this.name = name; this.workTime = workTime; this.latch = latch; } @override public void run() {system.out.println (name+" "+sdf.format(new Date())); doWork(); System.out.println(name+" format(new Date())); latch.countDown(); } private void doWork() {private void doWork() {// } catch (InterruptedException e) { e.printStackTrace(); }}}}Copy the code

It took two people three seconds to complete a task that would have taken five seconds. All I can say is, this programmer is incredibly efficient.

Second, the CyclicBarrier

A: What’s your barrier? Cyclic means cyclic, that is, the barrier can be used cyclic (which I’ll show you later). Source code official interpretation is:

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point . The barrier is called cyclic because it can be re-used after the waiting threads are released.

A group of threads will wait for each other until all threads have reached a synchronization point. This is very interesting, like a group of people stuck in front of a fence, and they can only break through the fence when the last person arrives.

A CyclicBarrier provides two ways to construct it:

public CyclicBarrier(int parties) {
    this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}Copy the code

The first constructed argument refers to the need for several threads to arrive before all threads can cancel the wait. The second construct, which specifies an additional parameter, takes precedence over the barrierAction when all threads reach the barrier.

Now, in a common scenario, a group of athletes race 1,000 meters, and only after everyone is ready to finish can they start running together (erm, ignore the details of the referee’s whistling).

Define a Runner class that represents an athlete and maintain a common CyclicBarrier internally, each of whom has a preparation time, and when it is ready, calls the await method and waits for the other athlete. When everyone is ready, it’s time to run.

public class BarrierTest { public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(3); // while (barrier, "三") = barrier; Runner 2 = new Runner(barrier, "三 "); Runner3 = new Runner(barrier, "三 "); ExecutorService service = Executors.newFixedThreadPool(3); service.execute(runner1); service.execute(runner2); service.execute(runner3); service.shutdown(); } } class Runner implements Runnable{ private CyclicBarrier barrier; private String name; public Runner(CyclicBarrier barrier, String name) { this.barrier = barrier; this.name = name; } @override public void run() {try {thread.sleep (new Random().nextint (5000)); System.out.println(name + ": ready OK"); barrier.await(); System.out.println(name +": run "); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e){ e.printStackTrace(); }}}Copy the code

As you can see, we have implemented the required functionality. However, some students are serious, said you this not ah, which athletes are ready to run, you also put the referee in the eye, the referee does not whistle, you dare to run a try.

Well, that’s the idea, so let’s make it happen and let the referee whistle before they start running together.

This is where the second constructor comes in, so I changed the code a little bit.

CyclicBarrier barrier = new CyclicBarrier(3, New Runnable() {@override public void run() {try {system.out.println ("... ); Thread.sleep(2000); thread.sleep (2000); System.out.println(" referee whistles ->>>>>"); } catch (InterruptedException e) { e.printStackTrace(); }}});Copy the code

Execution Result:

Zhang SAN: Ready OK Li Si: Ready OK Wang Wu: Ready OK wait for the referee to whistle... Referee whistles ->>>>> Zhang 3: run Li 4: run Wang 5: runCopy the code

As you can see, all three are ready, but they can’t start running until the referee has whistled.

I talked a little bit about how recycling works. I now change the barrier value to 2 and add a “zhao Six” to the race. The revised parts are as follows:

Then observe and print the result:

Zhang SAN: Ready OK Li Si: Ready OK wait for the referee to whistle... Referee whistling ->>>>> Li Four: open run Zhang Three: open run Wang Five: ready OK Zhao six: ready OK referee whistling... The referee whistles ->>>>> Zhao Liu: Run wang Wu: RunCopy the code

See? You can split it into two groups. The first group runs two, and then the second group runs two. The recycling of barriers.

Third, Semaphore

Semaphore, used to control the number of threads that can access resources at the same time, is generally used for flow control.

For example, now there is a section of highway traffic congestion, that how to do. At this point, the police need to come forward to limit the flow of the car.

For example, now there are 20 cars to pass through this section, the police uncle stipulated that a maximum of 5 cars can pass at any one time, the other cars can only wait. Only vehicles with permits are allowed to pass, and when they pass, the permits are returned and then given to waiting vehicles, who then pass, and so on.

public class SemaphoreTest { private static int count = 20; public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(count); Semaphore = new Semaphore(5); Semaphore = new Semaphore(5); Random random = new Random(); for (int i = 0; i < count; i++) { final int no = i; Executorservice.execute (new Runnable() {@override public void run() {try {// Obtain permission semaphore.acquire(); System.out.println(no +": "); Thread.sleep(random.nextint (2000)); Semaphore.release (); } catch (InterruptedException e) { e.printStackTrace(); }}}); } executorService.shutdown(); }}Copy the code

I’m not going to print it out, but if you look at it for yourself, you’ll see that the first batch was five cars going through at the same time. Then, the cars behind can pass in turn, but not more than 5 vehicles can pass at the same time.

Careful readers will find that there are only 5 licenses to be issued. Who should be issued in the second batch after the first batch of vehicles are used up and released?

That’s a real problem. All the waiting vehicles want to get permission first, first pass, what to do. And that’s where the lock comes in. Just grab everyone, and whoever gets it first gets to go.

If we look at Semaphore’s constructor, we can pass in a Boolean argument that controls whether lock grab is fair or not.

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}Copy the code

The default is unfair and you can pass true to use a fair lock. (The lock mechanism is through AQS, I will not go into details now, I will update later)

Here simply say, what is fair is not fair.

Well, if it’s fair, when your bus arrives, you just go first come, first served. What’s not fair, perhaps, is that some driver has big shoulders, a fancy watch and a thick gold chain around his neck. Others a look to provoke, I also can’t hide it, give this big brother way. Even if you walk in front of him, you dare not rob him.

In the end, he got the permit first. The rest of them go for it, maybe another one, a personal friend of the policeman’s uncle. (All right, all right, the other drivers can only be a face of life is not love, how to cross the road is so hard ah…)

conclusion

  1. CountDownLatch is a thread waiting for other threads, and CyclicBarrier is multiple threads waiting for each other.
  2. The count of CountDownLatch is minus one until zero, and CyclicBarrier is plus one until the specified value.
  3. CountDownLatch is one-time and CyclicBarrier can be recycled.
  4. CyclicBarrier can choose to perform an operation before the last thread reaches the barrier.
  5. Semaphore requires a license to execute and can choose between fair and unfair modes.

If this article is useful to you, please like it, comment on it, and retweet it.

Learning is boring and interesting. I am “Misty rain sky”, welcome to pay attention to, can be the first time to receive the article push.