juc - CyclicBarrier source code interpretation

I saw CountDownLatch before. His implementation is to use aqs to set the value of state in advance. If the state is not 0, calling await will block the current thread, adding it to the queue of aqs, and calling countDown will reduce the value of state. , when the value of state is 0, the lock will be released, all the threads in the queue will be released and start running. This CountDownLatch can be used between different types of threads. For example, when we connect zk, because another thread is used inside zkClient to link zk, our main thread can only continue to operate after zk is connected, so CountDownLatch is used. , which is used in different kinds of threads. CountDownLatch can also be used in the same kind of thread. For example, there is such a requirement: going to a restaurant to hold a birthday party, this party must wait for all friends to come before it can start together, at this time, you can use the following code:

public static void main(String[] args) {
		
		final CountDownLatch latch = new CountDownLatch(10);
		
		for(int i=0;i<10;i++){
			new Thread(new Runnable() {
				
				public void run() {
					System.out.println(Thread.currentThread().getName() + "It has come.");//A thread has come, representing a customer
					latch.countDown();//Indicates that another person has come
					try {
						latch.await();//The current person is waiting for others to come
						System.out.println("Since everyone is here, let's start");//When everyone is here, start
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace ();
					}
				}
			}).start();
		}
	}
	

 In the above code, CountDownLatch is used in threads of the same type, indicating that all threads will not execute the next step until they reach a certain state. There is a more useful class in the jdk that provides a similar function and is simpler, the CyclicBarrier class. The CyclicBarrier class represents a barrier, and when constructing this class, a value must be passed to indicate the size of the marker. When the condition is not met, all threads calling await will be suspended, and a flag will be decremented by one, and then the flag will be changed to 0, and all blocked threads will be made executable. Let's take a look at his source code:

1. Construction method:

public CyclicBarrier(int parties, Runnable barrierAction) {//parties indicates the number of await calls to be satisfied, that is, the number of people who must come to the party to be held as mentioned above. The second parameter is an executable runnable, which means that when the condition is satisfied (that is, after the partie becomes 0), one thing to do is executed by the thread that finally triggered await.
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;//This is unchanged for a method to return.
    this.count = parties;//This count will continue to decrease, and it will decrease by one every time awati is called.
    this.barrierCommand = barrierAction;//The task to be executed after the condition is met
}

There are many properties in this class,

    ReentrantLock lock  : Used to lock when calling await to prevent inaccuracy from concurrently modifying the value of count.

     Condition trip: The condition generated from the lock above is used to put the thread calling await into the waiting queue when the condition is not met, and move all the waiting to the queue of the lock (internally aqs) when the condition is met middle.

     Generation generation: This property is very simple, because CyclicBarrier is a reusable barrier. This generation represents the number of times it is used. When the parties become 0, the generation is over, and the next generation operation is performed. It encapsulates a boolean property, indicating whether the current generation has been destroyed. When a blocked thread is called interrupt will become true, that is, the current generation is destroyed.

 

2. await method

public int await() throws InterruptedException, BrokenBarrierException {
   try {
       return dowait(false, 0L);//Call from await is dowait without waiting time limit
   } catch (TimeoutException toe) {
       throw new Error(toe); // cannot happen;
   }
}

 The first parameter indicates whether there is a time limit, the second parameter is the time limit if there is a time limit

  private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();//Because the count needs to be modified, it is locked to prevent concurrent modification
        try {
           final Generation g = generation;
           if (g.broken)
               throw new BrokenBarrierException();//I have no research on broken, so I will not comment on the broken situation.

           if (Thread.interrupted()) {
               breakBarrier();
               throw new InterruptedException();
           }
           int index = --count;//Reduce count,
           if (index == 0) { // It will not be 0 when it comes up, this is triggered after the specified number of calls
               boolean ranAction = false;
               try {
                   final Runnable command = barrierCommand;//Task to be executed
                   if (command != null)
                       command.run();//Called by the thread that calls await last. Note that an exception may be thrown when run is executed, and it will enter the finally beakBarrier method, and other threads will all throw a BrokenBarrierException after re-execution.
                   ranAction = true;
                   nextGeneration();//Open the next generation, the operations include: 1. Reset count to parties, 2. Update the value of generation, 3. Release all threads blocked in the condition
                   return 0;
               } finally {
                   if (!ranAction)
                       breakBarrier();
               }
           }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {//This is when await is not called the specified number of times
                try {
                    if (!timed)//If there is no time limit, call condtion.await, that is, put it into the queue of conditon, and suspend the thread
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);//If there is a time limit, wait for the specified time,
                } 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)//If it is broken for some reason, all threads after waking up will throw an exception
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {//If there is a time limit, an exception will be thrown if the wait times out.
                    breakBarrier();//This method will set the generation to broken, and then release all the threads blocked in the condition.
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

 

This class is easy to understand without considering barrierBroken.

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326519526&siteId=291194637