Hello, I am Xiao Hei, a migrant worker who lives on the Internet.

In the previous installment, we talked about thread.join () and CountDownLatch, both of which can wait for the current Thread to finish executing. CountDownLatch is better and can wait for multiple threads at the same time. We know CountDownLatch is implemented via AQS by looking at the source code.

So what else is in the java.util.Concurrent package besides concurrency control tools like CountDownLatch? Let’s take a look today.

CountDownLatch

A synchronization aid that waits for one or more threads until a set of operations performed in the thread completes.

CountDownLatch is literally a “counter.” Looking back from yesterday, CountDownLatch can implement waiting for other threads to execute and can specify the waiting time.

For example, for an exam, the teacher has to wait for the students to arrive at the examination room before the test starts. If all the students arrive early, the teacher can hand out the test papers in advance. However, if there are students who do not arrive at the test time, the teacher can wait and start directly.

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {

        CountDownLatch count = new CountDownLatch(5);
        for (int i = 1; i <= 5; i++) {
            new Student("Students" + i, count).start();
        }
        // Wait only 5 seconds, then the test will be handed out
        count.await(5, TimeUnit.SECONDS);
        System.out.println("All students have arrived and the teacher has started handing out papers."); }}class Student extends Thread {
    private final CountDownLatch count;

    public Student(String name, CountDownLatch count) {
        super(name);
        this.count = count;
    }
    @Override
    public void run(a) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName() + "Arriving at the examination room ~");
            count.countDown();
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

First let’s look at the code. In the main thread, the teacher waits for 5 seconds, so the exam starts after 5 seconds. Each Student will sleep() for 2 seconds in the run method, simulating each Student’s arrival takes at least 2 seconds;

According to the results, all 5 students arrived at the examination room before the teacher developed and distributed the test papers, indicating that the time for the 5 students to arrive at the examination room was not more than 5 seconds, so it is certain that the 5 threads are not executed in serial.

The teacher waited and did begin the test; If the teacher’s waiting time is adjusted slightly, or the time for a thread to arrive at the examination room is increased, it will be found that the examination papers will be handed out before arriving at the examination room. For reasons of length, the code will not be included here. You already know the use of CountDownLatch from this example.

Semaphore

A counting semaphore. Conceptually, semaphores maintain a set of licenses.

Semaphore literally translates as Semaphore. Semaphores are usually used to limit the number of threads, not to restrict access to some shared resource.

We still through simulation examples from the life to, for example, let’s go to the ferris wheel, can accommodate the number of visitors on a ferris wheel is fixed, so need to see first before someone go up if there are the rest of the space, if you have the release, if not let visitors waiting, until someone from the ferris wheel on the left. We use Semaphore to simulate this scenario.

public class SemaphoreDemo {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3.true);
        for (int i = 1; i <= 5; i++) {
            new Visitor("Tourists"+ i, semaphore).start(); }}}class Visitor extends Thread {
    private Semaphore semaphore;

    public Visitor(String name, Semaphore semaphore) {
        super(name);
        this.semaphore = semaphore;
    }

    @Override
    public void run(a) {
        try {
            semaphore.acquire();
            System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "I'm so happy to be on the ferris wheel.");
            TimeUnit.SECONDS.sleep(2);
            System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Down from the Ferris wheel -------");
            semaphore.release();
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

From the running result, we can see that tourists 1,2 and 3 sit on the platform at the same time, but tourists 4 and 5 do not sit on the platform at this time. Tourists 4 and 5 can only get on the platform after someone gets off. Because we only gave 3 licenses when we created Semaphore, when 1,2, and 3 were occupied, 4 and 5 were not available and had to wait for licenses to become available again.

Let’s take a look at some of Semaphore’s methods.

A constructor

// Specify the number of licenses
new Semaphore(3);
// Specify the number of licenses and set the waiting thread to obtain licenses in a fair manner
new Semaphore(3.true);
// Specify the number of permissions and set the waiting thread to obtain permissions unfairly. Default is false
new Semaphore(3.false);
Copy the code

Members of the method

// Get permission, only 1 by default, block until successful, or the thread interrupts
semaphore.acquire();
// Obtain a given number of permits, block until successful, or the thread interrupts
semaphore.acquire(2);
// Same as acquire(), but cannot be interrupted
semaphore.acquireUninterruptibly();
// Same as acquire(2), but cannot be interrupted
semaphore.acquireUninterruptibly(2);
// Try to obtain a license, return true on success, false immediately on failure
semaphore.tryAcquire();
// Try to obtain a given number of licenses, return true on success, false immediately on failure
semaphore.tryAcquire(2);
// Try to obtain 1 license until timeout, return true on success, false on failure
semaphore.tryAcquire(5, TimeUnit.SECONDS);
// Try to obtain a given number of licenses until timeout, return true on success, false on failure
semaphore.tryAcquire(2.1, TimeUnit.SECONDS);
// Release 1 license
semaphore.release();
// Release a given number of permits
semaphore.release(2);
Copy the code

As those of you who have read my previous AQS source code can guess, Semaphore’s underlying implementation is also AQS, using a shared lock-related implementation of AQS.

Those of you who are interested can review this article.

CyclicBarrier

A synchronization aid that allows a group of threads to all wait for each other to reach a common barrier point.

A CyclicBarrier is literally a “circular barrier” and can be understood as a barrier that can be recycled. It waits for a set of threads to complete their execution before proceeding to the next step.

I have so many examples that it drives me crazy.

We will come into the arena and have dinner with friends, that’s the rules of the lake, we have to wait for everyone to arrive, can start to eat, and so on everyone to eat almost, we leave together. Did you notice that the example on the exam above is a little bit like that.

Let’s simulate this scenario using a CyclicBarrier.

public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        for (int i = 1; i <= 3; i++) {
            new BBQer("Version"+ i, cyclicBarrier).start(); }}}class BBQer extends Thread {
    CyclicBarrier cyclicBarrier;

    public BBQer(String name, CyclicBarrier cyclicBarrier) {
        super(name);
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run(a) {

        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Reached the battlefield.");
			// Wait for others to arrive
            cyclicBarrier.await();
            System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Hungry and ready to fight.");
            TimeUnit.SECONDS.sleep(1);
			// Wait for others to finish eating
            cyclicBarrier.await();
            System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Eat and drink, go home and sleep.");
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

This is very different from CountDownLatch. CountDownLatch is a counter that can only be counted once and can’t be zeroed after that. A CyclicBarrier sets this barrier in a cycle.

Let’s take a look at some of the commonly used methods of CyclicBarrier.

A constructor

// Create a loop barrier with a specified number of threads
new CyclicBarrier(3);
// Create a circular barrier with a specified number of threads, and run Runnable's run() method when all threads reach the barrier
new CyclicBarrier(3, Runnable);
Copy the code

Members of the method

// Wait for all other threads to arrive
cyclicBarrier.await();
// Wait for all threads to arrive
cyclicBarrier.await(1, TimeUnit.SECONDS);
// Get the number of threads currently waiting
cyclicBarrier.getNumberWaiting();
// Reset the wait state to the initial state
cyclicBarrier.reset();
Copy the code

Phaser

A reusable synchronization barrier that functions like CyclicBarrier and CountDownLatch but supports more flexible use.

Phaser literally means’ Phaser ‘. More flexible than cyclicbarriers and countdownlatches.

Let’s say that just as we were starting to eat, another friend called to say that he was coming too. We should ask him to come and eat with us and leave together.

This is not possible with CountDownLatch, CyclicBarrier, or Semaphore. Let’s take a look at Phaser.

public class PhaserDemo {

    public static void main(String[] args) throws InterruptedException {
        // We started out with three people
        Phaser phaser = new Phaser(3);
        for (int i = 1; i <= 3; i++) {
            new Foodie("Version" + i, phaser).start();
        }
        TimeUnit.SECONDS.sleep(2);
        phaser.register();
        new Foodie("New boy", phaser).start();
        System.out.println("Number of people for dinner:"+ phaser.getRegisteredParties()); }}class Foodie extends Thread {
    private Phaser phaser;

    public Foodie(String name, Phaser phaser) {
        super(name);
        this.phaser = phaser;
    }

    @Override
    public void run(a) {
        try {
            phaseOne();
            phaseTwo();
            phaseThree();
        } catch(Exception e) { e.printStackTrace(); }}public void phaseOne(a) throws InterruptedException {
        // The newcomer doesn't have to wait for anyone else
        if (Thread.currentThread().getName().equals("New boy")) {
            return;
        }
        TimeUnit.SECONDS.sleep(1);
        System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Reached the battlefield.");
        // Arrive at the meal and join the wait
        phaser.arriveAndAwaitAdvance();
    }

    public void phaseTwo(a) throws InterruptedException {
        System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Hungry and ready to fight.");
        TimeUnit.SECONDS.sleep(2);
        System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Finished.");
        phaser.arriveAndAwaitAdvance();
    }

    public void phaseThree(a) {
        System.out.println(LocalDateTime.now() + Thread.currentThread().getName() + "Go home and sleep."); phaser.arriveAndDeregister(); }}Copy the code

From the running result, we can see that at the beginning, 1,2 and 3 have started to fight (finished the first stage and entered the second stage), this is later when a new friend joined, the number of people in the dinner became 4, and then gradually completed the next stage.

Let’s also look at Phaser methods.

A constructor

// Create a stager with no registrars, no parent and no initializer
Phaser()
// Create a stager that specifies the number of registrars
Phaser(int parties)
Phaser(Phaser parent, 0)
Phaser(Phaser parent)
// Create a stager with the given parent stager and the given number of registrants
Phaser(Phaser parent, int parties)
Copy the code

Members of the method

// Arrive at this stage without waiting for others to arrive
arrive()
// Arrive and block waiting for others to arrive
arriveAndAwaitAdvance()
// Arrive and log out
arriveAndDeregister()
// Wait to enter from the specified phase and return immediately if it is not in that phase or if the stager has terminated
awaitAdvance(int phase)
// Same as awaitAdvance(int phase), which can be interrupted
awaitAdvanceInterruptibly(int phase)
// Same as awaitAdvance(int phase), can be interrupted, can timeout
awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit)
// Add a new node that has not been reached
register()
// Add a specified number of new nodes that have not been reached
bulkRegister(int parties)
// Force to terminate the stager
forceTermination()
// Get the number of arrivals
getArrivedParties()
// Get the parent stage
getParent()
// Get the root stager
getRoot()
// Returns the current phase number
getPhase()
// Return the registered node
getRegisteredParties()
// Returns the node that has not been reached
getUnarrivedParties()
// Determine whether to terminate
isTerminated()
// Overwrite methods that preemptively execute the action on the upcoming node and control the termination.
onAdvance(int phase, int registeredParties)
Copy the code

conclusion

To summarize, today’s focus is on thread synchronization tools in the JUC package.

CountDownLatch: mainly used for counting, which can complete the waiting for multiple threads to execute. The count decreases by 1 each time and releases the waiting thread when it reaches 0. Can not be reset after zero, can not be reused;

Semaphore: usually used to restrict access to resources, such as stream limiting, acquire through acquire() + 1, release() + 1, access will be blocked without permission;

CyclicBarrier: a CyclicBarrier, as opposed to the CountDownLatch, await() method, incrementing by 1 each time to the specified value to release the waiting thread; After the specified value will be reset, recyclable;

Phaser: Supports CountDownLatch and CyclicBarrier functions, can be replaced, and can dynamically add or decrease set points.


Ok, this content is over here, we will see you next time, pay attention to my public number [xiao Hei said Java], more dry goods content.