Good friend programmer, ran to move bricks

Posted by ph3n0m on Thu, 23 Dec 2021 05:09:19 +0100

1, The story begins

Time is in a hurry and time is unforgiving. With the growth of age, we have an increasing burden on our shoulders. In addition to our own work, we also want to earn some extra money through other ways to subsidize our family (our own small Treasury).

On that day, my good friend Xiaoqiang carried his daughter-in-law to move b ricks at the weekend to earn pocket money.

On the construction site, there are the Contractor's nephew A, the Contractor's sister-in-law B, the Contractor's brother-in-law C and Xiaoqiang.

Xiaoqiang was shocked when he saw that there were four TMD people and there were three related households. Do you have to rely on relationships to move a brick these days?

In order to make some money, Xiaoqiang is still ready to do it.

The contractor first conducted a test to test Xiaoqiang's strength and see how many bricks Xiaoqiang can move at a time. After the test, Xiaoqiang can only move three bricks at a time and move them from the brick kiln to the car.

The contractor begins to divide the work: A, B and C are responsible for putting the bricks in Xiaoqiang's hand, Xiaoqiang moves them to the car, and then cycle the process

Time passed quickly. Unconsciously, one day passed. Xiaoqiang was tired and went to the contractor to pay the bill. The contractor gave him 150 yuan. Xiaoqiang saw that it was late. There was no bus to go home, so he had to take a taxi. Then he took a taxi home for 100 yuan. In order to reward himself, he spent 100 yuan. When he went home at night, he found that he didn't make money, And 50 more.

Xiaoqiang, who gets more and more angry, takes out his computer and is ready to write a blog about today's experience to let the majority of netizens give themselves some comfort.

So he found his friend Xiao Ming to write a blog for him. As a technician, Xiao Ming certainly doesn't like writing in vernacular, so he directly wrote his brick moving experience into code. The code is as follows:

package com.example.democyclicbarrier;
import java.util.concurrent.CyclicBarrier;
/**
 * @author Discover more interesting official account: Muzi's day and night programming
 * A code farmer who lives at the bottom of the Internet and does addition, deletion, modification and inspection, is not familiar with the affectation of the world
 * @create 2021-08-15 10:07
 */
public class TestCyc {
    public static void main(String[] args) {
        System.out.println("Xiaoqiang is ready to make money...");
        System.out.println("Test Xiaoqiang's strength...");
        // Focus on code
        CyclicBarrier barr = new CyclicBarrier(3, ()->{
            try {
                System.out.println("That's enough. Xiaoqiang moved to the car...");
                Thread.sleep(2000);
            } catch (Exception e) {
                System.out.println("Xiaoqiang quit...");
            }
        });
        System.out.println("Test result: it can be moved at one time"+barr.getParties()+"block");
        System.out.println("Start moving bricks...");
        // Suppose you move five times a day
        new Thread(()->{
            for (int i = 0; i < 5 ; i++) {
                try{
                    System.out.println("A Put a brick in Xiaoqiang's hand.");
                    barr.await();
                    // Enough three blocks to execute the following code logic
                } catch (Exception e) {
                    System.out.println("A Quit");
                }
            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 5 ; i++) {
                try {
                    System.out.println("B Put a brick in Xiaoqiang's hand.");
                    barr.await();
                    // Enough three blocks to execute the following code logic
                } catch (Exception e) {
                    System.out.println("B Quit");
                }
            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 5 ; i++) {
                try {
                    System.out.println("C Put a brick in Xiaoqiang's hand.");
                    barr.await();
                    // Enough three blocks to execute the following code logic
                } catch (Exception e) {
                    System.out.println("C Quit");
                }
            }
        }).start();
    }
}

Output results:

Xiaoqiang is also bitter enough. Three people moved to his hand and he moved to the car alone. Isn't it true that we do back-end development??

Product + front end + test are connected to our back-end development.

The product has a demand. After the front-end code is written, it should be jointly debugged with our back-end. The test urges us to develop quickly and put it into test early. Our back-end can only do it like Xiaoqiang. Finally, I found that the money in my hand was not enough to support me to live....

A knowledge point is used in this example. CyclicBarrier literally means "circular fence"

The usage method has been written above, and it is basically used like that. To summarize its specific function, it is to set A basic value parties (that is, how many bricks Xiaoqiang can move at A time), give A callback function (that is, what Xiaoqiang needs to do after reaching the parties), and then the await method (that is, the work of A, B and C. await goes further from the callback at one time)

2, Analysis of knowledge

We might as well take a look at the source code

2.1 creating

// The number of thread waits set by parties, that is, it takes several await s to wake up the callback barrierAction execution
// barrierAction callback await will be executed when the number of threads reaches the number of parties
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    // Some people wonder what this count is for. It is an intermediate quantity. First set it to the value of parties
    // Then each await count will be subtracted by 1. You can see the figure above 
    // When count equals 0, the callback and -- "change the Dynasty" will be executed
    this.count = parties;
    this.barrierCommand = barrierAction;
}

This class has five important attributes to pay attention to:

    // When locking await, you need to obtain the lock before executing the logic code 
	// Why do you need a lock? You can't put bricks on Xiaoqiang's hands by more than a dozen people. Isn't that an abnormal situation directly
    // It's impossible to distinguish who is the first and who is the last. Maybe the last one will be executed multiple times 
    // In any case, multiple threads operate count. Generally, locks are required 
	private final ReentrantLock lock = new ReentrantLock();
    // This is a condition of lock lock. All threads are blocked through condition await
    // When the number of await s is sufficient, that is, when count =0, all threads will be signalAll
    private final Condition trip = lock.newCondition();
    // Needless to say, this is the maximum number of await
    private final int parties;
    // This callback is a Runnable 
    private final Runnable barrierCommand;
    // This is a Generation concept. How to say, every time Xiaoqiang moves bricks to the car, a new Generation will be created
	// The main parameter to carry the brick moving information is: whether the generation of broken mark is interrupted
    private Generation generation = new Generation();
	// This is the diligent counter who counts down from parties to 0
    private int count;

2.2 await

//  Return the index, that is, the thread of the first await. If index = parties - 1, it is the thread of the first await  
// If it is equal to 0, the last await thread will execute the callback function
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        // 
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        // Acquire lock 
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // Get current generation
            final Generation g = generation;
            // If the token broken of the current generation is true, throw the exception directly (don't focus on it)
            if (g.broken)
                throw new BrokenBarrierException();
			// If the thread is interrupted, set the flag broken to true (don't focus on it)
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
			// Here's the point
            // 1. count -- if the result is 0, execute callback and change the dynasty
            int index = --count;
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    // Execute callback: execute the Runnable of barrierCommand
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                     // Change of Dynasty: there are three main steps to execute nextGeneration
                    // Step 1: trip signalAll();  Notify all await threads to stop await and continue execution
                    // Step 2: count = parties; The counter starts counting down again from paeties
                    // Part III: generation = new Generation(); new new generation 
                    nextGeneration();
                    return 0;
                } finally {
                    // There are also three steps to directly terminate this generation in case of any exception:
                    // Step 1: generation broken = true
                    // Step 2: count = parties;
                    // Part III: trip signalAll();
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            // Loop waiting is awakened, this generation is broken, interrupted, or timed out (this is the parametric method of await, which can be set)
            // Wait timeout)
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    // If the thread is interrupted, this series of operations is called 
                    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();
                // Whether it is the current generation is crucial here
                // A thread can use multiple fences. It may be awakened by other fences. I don't understand this sentence 
                // I understand that it is actually the non current generation await thread that wakes up when the breakBarrier() method signalAll
                // If it is not equal, it indicates that the dynasty has changed and directly returns to index
                // If there is no change of Dynasty, you will be awakened by other generations. Hurry back to await and wait for your generation to awaken
                if (g != generation)
                    return index;
                // Timeout 
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            // finally unlock 
            lock.unlock();
        }
    }

Several small methods:

private void breakBarrier() {
    generation.broken = true;
    count = parties;
    trip.signalAll();
}

private void nextGeneration() {
    // signal completion of last generation
    trip.signalAll();
    // set up next generation
    count = parties;
    generation = new Generation();
}

About await and awaitNanos, this is Lock related knowledge, which will not be described here

Below is a flow chart:

3, Some things

I often see the comparison between CyclicBarrier and CountDownLatch in my blog. Personally, I don't think there is any comparison. If you read my two articles about them, you will understand them all.

4, Summary

Deeply cultivate and widely cultivate the knowledge in your field, so that you can rely on the knowledge in your field for a lifetime.

More attention to official account:

Topics: Java