Fuente CyclicBarrier explore (JDK 1.8)

CyclicBarrierTambién se llama valla de bucle, se puede lograr para permitir que un conjunto de subprocesos que se ejecutan en la valla y función de bloqueo hasta que todos los hilos han llegado a la valla y luego realizar juntos. "Loop" significa que CyclicBarrierse pueden utilizar en varias ocasiones, en comparación con CountDownLatchsólo pueden utilizarse una vez, CyclicBarrierse puede ahorrar una gran cantidad de recursos, y también pueden pasar tarea en el constructor, para llevar a cabo esta tarea cuando se cumplen las condiciones de la cerca. CyclicBarrierEs utilizado ReentrantLock, el método principal será bloqueado en el momento de la ejecución, por lo tanto, el rendimiento concurrente no es muy alto.

1. campos relacionados

    //重入锁,CyclicBarrier内部通过重入锁实现线程安全
    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();
    //当前还需要等待多少个线程运行到栅栏位置
    private int count;

Tenga en cuenta que el generationcampo utilizado para marcar la valla actual en la generación. Cuando se cumplen ciertas condiciones (por ejemplo, los llamados resetmétodos, etc., o se abre una valla), el estado cambia a la siguiente cerca, es en realidad newun nuevo Generationobjeto, que es CyclicBarrieruna clase interna, el código es muy simple, como sigue:

    private static class Generation {
        boolean broken = false;   //标记栅栏是否被破坏
    }

En el uso real, vamos a utilizar generationlos campos de determinar si la corriente en el mismo generacional, utilizando brokercampo determina si la barrera se destruye.

2. Constructor

CyclicBarrierHay dos constructor sobrecargado, el constructor simplemente relacionada con los campos anteriores son inicializados como sigue:

    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;
    }

3. El método de núcleo

  • await
    awaitEs el método más comúnmente utilizado de desarrollo de la misma CountDownLatch, al igual CyclicBarrierque también proporciona dos awaitmétodos, uno sin parámetros, con un parámetro de tiempo de espera, simplemente llame al interior de un poco de dowaitmétodo:
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

Luego tomar un aspecto crítico dowaitmétodo:

    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        //加重入锁
        lock.lock();
        try {
            //首先获取年龄代信息
            final Generation g = generation;
            //如果栅栏状态被破坏,抛出异常,例如先启动的线程调用了breakBarrier方法,后启动的线程就能够看到g.broker=true
            if (g.broken)
                throw new BrokenBarrierException();
            //检测线程的中断状态,如果线程设置了中断状态,则通过breakBarrier设置栅栏为已破坏状态,并唤醒其他线程
            //如果这里能够检测到中断状态,那只可能是在await方法外部设置的
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            //每调用一次await,就将需要等待的线程数减1
            int index = --count;
            //index=0表示这是最后一个到达的线程,由该线程执行下面的逻辑
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    //如果在构造器中传入了第二个任务参数,就在放开栅栏前先执行这个任务
                    if (command != null)
                        command.run();
                    ranAction = true;
                    //正常结束,需要唤醒阻塞的线程,并换代
                    nextGeneration();
                    return 0;
                } finally {
                    //try代码块如果正常执行,ranAction就一定等于true,而try代码块唯一可能发生异常的地方就是command.run(),
                    //因此这里为了保证在任务执行失败时,将栅栏标记为已破坏,唤醒阻塞线程
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    //没有设置超时标记,就加入等待队列
                    if (!timed)
                        trip.await();
                    //设置了超时标记,但目前还没有超时,则继续等待
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    //如果线程等待的过程中被中断,会执行到这里
                    //g == generation表示当前还在同一个年龄分代中,!g.broker表示当前栅栏状态没有被破坏
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        //上面的条件不满足,说明:1)g!=generation,说明线程执行到这里时已经换代了
                        //2)没有换代,但是栅栏被破坏了
                        //无论哪种情况,都只是简单地设置一下当前线程的中断状态
                        Thread.currentThread().interrupt();
                    }
                }
                //栅栏被破坏,抛出异常
                //注意,在breakBarrier方法中会唤醒所有等待条件的线程,这些线程会执行到这里,判断栅栏已经被破坏,都会抛出异常
                if (g.broken)
                    throw new BrokenBarrierException();
                //距离上一次设置g变量的值已经过去很长时间了,在执行过程中generation可能已经发生改变,
                //当前线程还是前几代的,不需要再循环阻塞了,直接返回上一代剩余需要等待的线程数
                //注意:代码中breakBarrier方法和nextGeneration方法都会唤醒阻塞的线程,但是breakBarrier在上一个判断就被拦截了,
                //因此走到这里的有三种情况:
                //a)最后一个线程正常执行,栅栏打开导致其他线程被唤醒;不属于当前代的线程直接返回,
                //属于当前代的则可能因为没到栅栏开放条件要继续循环阻塞
                //b)栅栏被重置(调用了reset方法),此时g!=negeration,全都直接返回
                //c)线程等待超时了,不属于当前代的返回就可以了,属于当前代的则要设置generation.broken = true
                if (g != generation)
                    return index;
                //如果线程等待超时,标记栅栏为破坏状态并抛出异常,如果还没超时,则自旋后又重新阻塞
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            //别忘了解锁
            lock.unlock();
        }
    }

dowaitLa lógica del método es: cada llamada awaitserá a la cuenta de hilos countreducido 1, el último hilo se countreduce a 0tiempo, dicho sea de paso, también llevan a cabo barrierCommandtareas y especificados generationcambiar a la siguiente generación, por supuesto, lo más importante es despertar antes de la valla en hilo bloqueado. Debido a tripla correspondiente Conditionobjeto no tiene lugar será modificada, por lo que trip.signalAll()será todo despertar en la condición de espera de hilo, si el hilo en el proceso de esperar, los otros hilos se generationactualizará a la siguiente generación, habrá hilo despertado algunos también pertenecen antes de esa situación generación.
La próxima será en dowaitalgunos de los métodos utilizados para una breve introducción.

  • breakBarrier
    dowaitHay cuatro llamado método local breakBarrier, se puede ver en el nombre, que se generation.brokenestablece true, además, restaurará countlos valores y se despierta todo el hilo bloqueado:
    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        //唤醒所有的阻塞线程
        trip.signalAll();
    }

Mirando el CyclicBarriercódigo fuente, generation.brokenunificada breakBarrierse establece en el enfoque true, pero una vez generation.brokenestablecido truemás tarde, después de comprobar el código va a lanzar una excepción a este estado, no hay manera de volver a utilizar la cerca (se puede llamar manualmente resetreinicio), y el código fuente en las siguientes situaciones llamará al breakBarriermétodo:
1) el hilo de corriente se interrumpe
2) pasado a través de la tarea constructor falla
condición interrumpida 3) esperando
4) hilo espera de tiempo de espera
5) llamar explícitamente al resetmétodo

  • nextGeneration
    private void nextGeneration() {
        // 唤醒所有的阻塞线程
        trip.signalAll();
        // 开启下一代
        count = parties;
        generation = new Generation();
    }
  • reset
    resetEl método principal es el final de esta generación, y el interruptor a la generación siguiente
    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

Introducido aquí, en su conjunto CyclicBarrierse ha introducido a punto de terminar, pero el proceso interno está lejos de ser simple, porque la lógica se encapsula en una gran parte AbstractQueuedSynchronizer, esto define la clase cómo se bloquean los hilos en espera de la cola, sino también cómo ser despertado, así que si quieres hilo lógico visión que esperar, pero también hay que estudiar detenidamente AbstractQueuedSynchronizerel trabajo. En este artículo se describe esta parte del contenido no estará detrás de si tiene tiempo será una presentación especial.

Supongo que te gusta

Origin www.cnblogs.com/NaLanZiYi-LinEr/p/12484022.html
Recomendado
Clasificación