JUClock the circulation barrier CyclicBrrier

Disclaimer: if reproduced - please add the next micro-channel can inform 457,556,886 knowledge is shared https://blog.csdn.net/wolf_love666/article/details/89357350

Objective: To allow a set of threads waiting for each other to reach a common barrier point.

Circulation barrier can achieve a fixed set of threads waiting for each other occasionally. This barrier is called a cycle, because after waiting thread is released it can be reused.
To complete the task before it reaches the barrier point release thread in this group all threads, and the last thread Upon arrival, each thread loop support a barrier point barrier once thread execution command.

Metaphor: Whether a freshman or sophomore, third-year students. I set up to be the first to sign, they can wait for everyone to finish the playground collection, and then start doing exercise. And this process for each student to sign on to be understood as a barrier point. Everyone needed once, the last sign before the student arrives later, everyone will do a sign, after his attendance, everyone doing exercise together.
Suppose now that there is a student fell due to an abnormal situation (interruption), heat stroke (abnormal), has not come (time-out), the barrier will loop through BrokenBarrierException or InterruptedException so that other students do not first while doing exercise. Maintain consistency.

Each time a barrier is considered to be generated once instance. Any time a barrier is triggered or is reset, will change. This also led to many generations there will be instances associated with the thread when using this barrier. Because of this uncertainty the way, this lock may be assigned to the waiting thread (but only only one can be active at a time) and all the rest of the threads either interrupted or is triggered. If you already have an interrupt but no later reset, no instance of an active here.

Simple to understand the above paragraph: The barrier can only be used once, when a group of threads processed simultaneously execute only one thread at a time with other threads either break or trigger, exhausted after want to achieve circulation barrier, need to reset the instance .

Source resolve

package java.util.concurrent;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
JDK版本1.5出现 类似CountDownLatch
public class CyclicBarrier {
 private static class Generation {
        boolean broken = false;
    }
    用来保证屏障入口的锁
  private final ReentrantLock lock = new ReentrantLock();
  条件信号量,条件等待直到触发
  private final Condition trip = lock.newCondition();
  固定的线程数
  private final int parties;
  当触发的时候这个命令运行
  private final Runnable barrierCommand;
  当前的实例
  private Generation generation = new Generation();
  仍然等待的线程数,从固定的线程数根据每个实例执行依次递减为0.
  每重新生成一个实例或者中断一个线程,它都会被重置数量
  private int count;
  更新屏障触发的状态,并且通知所有其他线程
  仅仅当持有锁的时候被调用
  private void nextGeneration() {
        // 上一个实例完成的信号
        trip.signalAll();
        // 设置下一个实例
        count = parties;
        generation = new Generation();
    }
    将当前线程设置为中断链接,并通知所有其他线程
    仅仅当持有锁的时候被调用
    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }
    主要的屏障代码,覆盖了实现屏障的各种策略
    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
               定义一个可重入锁
        final ReentrantLock lock = this.lock;
        加锁保证只有一个线程持有锁
        lock.lock();
        try {
        	生成一个代实例
            final Generation g = generation;
			判断当前代实例是否被中断,如果中断抛出自己的异常中断屏障异常
            if (g.broken)
                throw new BrokenBarrierException();
			判断当前线程是否被中止
            if (Thread.interrupted()) {
                如果当前线程中止,设置当前线程状态为中断,并且唤醒其他所有线程
                breakBarrier();
                throw new InterruptedException();
            }
			组内线程数递减1
            int index = --count;
            如果下标为0
            if (index == 0) {  // tripped
                触发执行屏障点的线程
                boolean ranAction = false;
                try {
                	屏障点执行命令
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    执行完毕
                    ranAction = true;
                    生成下个代实例
                    nextGeneration();
                    return 0;
                } finally {
                	如果中途没有执行完毕,则设置当前线程状态为中断,并且唤醒其他所有线程
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // 循环直到被触发,中止,中断,或者超时
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                       即使没有被中断,我们打算完成等待,因此这里属于后续操作
                        Thread.currentThread().interrupt();
                    }
                }
				如果被断开链接,抛出BrokenBarrierException
                if (g.broken)
                    throw new BrokenBarrierException();
				如果代在中途被修改了,返回下标
                if (g != generation)
                    return index;
				如果超时了,中断屏障,抛出超时异常
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
        	释放持有的锁
            lock.unlock();
        }
    }
    创建一个新的循环屏障,用于触发设置的这个组内的线程,并且将会执行这个被给定的屏障行为barrierAction,当这个屏障被触发时候,由进入屏障的最后一个线程执行
	   public CyclicBarrier(int parties, Runnable barrierAction) {
	   如果组内线程数parties小于1,抛出非法参数异常
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        如果没有任何操作则为null,如果屏障点被触发,这个命令执行
        this.barrierCommand = barrierAction;
    }
    创建一个新的循环屏障,用于触发设置的这个组内的线程,但是在触发的时候不执行任何预定义操作,其实就是上面的方法,没有指定线程执行的行为
     public CyclicBarrier(int parties) {
        this(parties, null);
    }
    返回这个屏障点卡着的线程数量
    public int getParties() {
        return parties;
    }
    直到所有的屏障点卡着的线程数量(上面的那个方法)都已经执行await方法在这个屏障点。
    如果当前线程不是最后到达的,那么他将禁止所有线程调度并且休眠,直到下列条件有一种情况的发生:
    @1、最后的一个线程到达,或者一些其他线程中断当前线程
    @2、一些其他线程中断其他等待线程
    @3、一些线程超时当等待屏障的时候
    @4、一些线程在这个屏障点执行了重置
    如果这个当前线程有他中断的状态设置在进入到这个方法。或者是中断当等待的时候,那么中断异常会被throw出来并且当前线程的中断状态被清除掉。
    如果这个屏障被重置当其他线程等待的时候,或者这个屏障被中断链接,当执行await方法的时候,或者当其他线程正在等待,那么BrokenBarrierException被抛出
    如果当等待的时候,如果任意线程被中断,那么其他所有的等待线程都会抛出BrokenBarrierException异常,并且这个屏障被中断状态所替代。
    如果当前线程是最后到达的线程,和构造函数中提供了非null barrier操作,那么这个当前线程在允许其他线程继续之前运行这个操作。
    如果一个异常在屏障行为执行期间发生,那么这个异常将在当前线程传递,并且这个屏障会被中断状态所替代。
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
        当前线程的到达索引,其中索引{@code getParties() - 1}表示第一个到达,0表示最后到达
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
        不可能发生
            throw new Error(toe); 
        }
    }
    与上面类似,只是增加了时间限制。指定的等待时间已经过了
    timeout:等待屏障的时间,unit超时参数的时间单位
    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }
    查询这个屏障是否是中断状态
    public boolean isBroken() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return generation.broken;
        } finally {
            lock.unlock();
        }
    }
    重置这个屏障的初始状态,如果其他组线程是当前等待在这个屏障,他们将返回一个BrokenBarrierException异常。需要注意的是:在由于其他可能很复杂原因,已经发生异常的情况下可能重新建一个比重置好的多。
     public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        中断当前的代实例
            breakBarrier();  
            开启一个新的代实例
            nextGeneration(); 
        } finally {
            lock.unlock();
        }
    }
    返回正在屏障等待的线程数量,这个方法主要用于debug的时候和断言的时候
    public int getNumberWaiting() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return parties - count;
        } finally {
            lock.unlock();
        }
    }
    }

Case realization

package com.xiaochengxinyizhan.interview.threadcoding.lock;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;


public class CyclicBarrierDemo {
    public static void main(String args[]) throws InterruptedException{
        final float[][] data = new float[][]{{1,2},{2,3},{3,4},{4,5}};
        Solver solver=new Solver(data);
    }

}

class Solver {
    //数据值
    final int N;
    //需要计算的数组
    final float[][] data;
    //循环屏障
    final CyclicBarrier barrier;
    //创建一个值接受所有数据最终结果值
    ConcurrentHashMap<Float,Float> reslut = new ConcurrentHashMap<Float,Float>();
    //工人
    class Worker implements Runnable {

        int myRow;

        Worker(int row) {
            myRow = row;
        }

        public void run() {
                try {
                    float count1 =0;
                    float [] floats=data[myRow];
                    for (float count:floats){
                        count1+=count;
                    }
                    reslut.put(Float.valueOf(myRow),count1);
                    barrier.await();
                } catch (InterruptedException ex) {
                    return;
                } catch (BrokenBarrierException ex) {
                    return;
                }
            }
    }

    public Solver(float[][] matrix) throws InterruptedException{
        //需要计算的数组值
        data = matrix;
        //由于worker只计算单行内容,所以数组的长度就是需要执行的线程的个数
        N = matrix.length;
        //屏障执行命令 最后一个到达的时候
        Runnable barrierAction =
                new Runnable() {
            @Override
                    public void run() {
                        float conumMerge=0;
//                        mergeRows(...);
                        Enumeration em=reslut.keys();
                        while(em.hasMoreElements()){
                            conumMerge += reslut.get(em.nextElement());
                        }
                        System.out.println("总和:{}"+conumMerge);
                    }
                };
        //规定屏障固定线程数和最后一个线程到达后执行的命令行为
        barrier = new CyclicBarrier(N, barrierAction);
        //创建线程个数list集合
//        List<Thread> threads = new ArrayList<Thread>(N);
        //遍历线程个数执行线程
        for (int i = 0; i < N; i++) {
            Thread thread = new Thread(new Worker(i));
            //将所有执行的线程worker都放到线程个数list集合里面
//            threads.add(thread);
            //启动线程
            thread.start();
        }

        // wait until done
//        for (Thread thread : threads){
//            thread.join();
//        }
    }
}

summary:

Atomic updates the counter, and queue condition variables to achieve using a thread synchronization is achieved by an exclusive lock ReentrantLock.
Circulation barrier principle: Let a set of threads all achieve a state before all at once, where reason is called loop because it can be reused when all the waiting threads is finished, and after the reset state CyclicBarrier, the reason is called the barrier will be blocked because the thread calls await method, this point is called a barrier blocking point, and so all the threads call the await method, they will be out of the thread barrier, continued to run down.
So what countDownLatch not realize that? A thread is assumed to implement step 3, B 3 step thread implementation. A B thread synchronization with each step. Here Insert Picture DescriptionParties here are used to record the number of threads, it said here after how many threads call await, all threads will continue to break down barriers. And count the outset equal parties, whenever a thread calls await method decrements, when the count value is equal to 0 means that all of the threads to the barrier point. So why do not maintain a value, because CycleBarier can be reused, the reason for using two variables is, the total number of parties always used to thread the record, when the count value of the counter becomes zero, the parties will be assigned a value to COUNT, thereby performing multiplexing, two arguments are passed in this configuration the object cyclicBarrier time.
barriercommand is passed through the constructor, which is a task execution timing of this task is when all threads have reached the barrier point, use the Lock first ensure the atomicity of update counter count. In addition the use of Lock condition variable trip to support the use of inter-thread synchronization await and signal operations.
Whether the barrier is broken through, broken here and have not been declared as a volatile, as is the use of variables in the lock, so do not need to declare

Guess you like

Origin blog.csdn.net/wolf_love666/article/details/89357350
Recommended