Cómo CyclicBarrier es llegar a ser una "cerca" de

CyclicBarrierHay una valla similares, lo que significa que antes de abrir la valla sólo puede ser bloqueada en un lado de la valla, cuando se retira la valla, antes de ser bloqueado en un lado de la pluralidad de objetos a la vez que se inicia a moverse hacia arriba.

1. Cómo utilizar CyclicBarrier

  Antes de la introducción del principio, primer vistazo a CyclicBarrierla forma en que se debe utilizar.

  Supongamos ahora que hay un tal escenario, tenemos que abrir una reunión , es necesario Zhang 1, 2 Zhang, Zhang 3 tres personas para asistir,
la reunión después de tres personas estuvieron presentes para comenzar o sólo espera seco; esta escena con CyclicBarrierpuede ser muy conveniente simulado. Código es el siguiente:

public static void main(String[] args) {
    // 线程池,每个线程代表一个人
    ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
    // 会议所需的人数为3
    CyclicBarrier barrier = new CyclicBarrier(3);

    executor.execute(() -> {
        try {
            System.err.println("张1到达会议室");
            barrier.await();
            System.err.println("会议开始,张1开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张2到达会议室");
            barrier.await();
            System.err.println("会议开始,张2开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张3先去个厕所,内急解决再去开会");
            TimeUnit.SECONDS.sleep(1);
            System.err.println("张3到达会议室");
            barrier.await();
            System.err.println("会议开始,张3开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });


    executor.shutdown();
}

Resultados Figura:
Ejemplo de la figura.
  a través de la parte superior del código puede conocer CyclicBarrieralgunos puntos:

  1. Utilizar await()para indicar la finalización de ciertas cosas. (Ejemplo anterior rendimiento llegó a la sala de conferencias )
  2. Uso await()después de la hebra actual entra en el estado bloqueado , necesidad de esperar a satisfacer plenamente CyclicBarrierlas condiciones de la estela para continuar con la siguiente operación. (En el ejemplo anterior tres individuos han llegado a la sala de conferencias )
  3. Después de que el último hilo para lograr la condición antes de bloquear todas las discusiones abiertas siguen la siguiente operación. (El ejemplo anterior es Zhang alcance 3 sala de conferencias)

  Este sencillo ejemplo también nos permiten saber CyclicBarrierusando el método, y que es mirar en su interior exactamente cómo lograr el efecto de la valla.

2. CyclicBarrier ¿Cómo es una "cerca" de

  Podemos ver en el primer código, tenemos que centrarnos en dos lugares

  1. constructor
  2. await método ()

Mientras estos dos métodos para comprender la interna, que es equivalente a entender el CyclicBarrierinterior.
Antes de ahondar en que, en primer vistazo a CyclicBarriervarias variables, no tiene que recordar, cuando se mira en el código para saber qué hacer con esta cosa en la línea:

Bloqueo:CyclicBarrier clase crea la ReentrantLockinstancia, acerca ReentrantLockclaro lata -> transferencia.

viaje:lock en condition, CyclicBarrierutilizar la variable de lograr el bloqueo y despertar al mismo tiempo entre los hilos . Del mismo modo, hay una clara conditionacción => Portal .

partes: la necesidad de satisfacer las condiciones (llaman awaitel número de formas), y es entonces cuando hay partidos esperan hilos () se despierta todos los hilos.

barrierCommand: una Runnablevariable en el awaitnúmero total de llamadas al método para llegar partiesdespués, antes del despertar todos los hilos ejecutar su run()método

generación: sus clases internas pueden ser entendidas como un período, completar el ciclo n tareas, siempre y cuando falla una tarea, todas las tareas de la época actual, incluso si la falla para terminar el ciclo actual, y luego gire el próximo ciclo.

contar: el período actual el número de tareas que quedan por hacer (la llamada restante awaitmétodo del número)

La siguiente es la fuente:

public class CyclicBarrier {
    // 内部类,可理解为周期
    private static class Generation {
        // 当前周期是否失败
        boolean broken = false;
    }

    // 锁的实例
    private final ReentrantLock lock = new ReentrantLock();
    // ReentrantLock的condition变量,用来控制线程唤醒和阻塞
    private final Condition trip = lock.newCondition();
    // 需要满足条件的次数,即需要调用await方法的次数
    private final int parties;
    // 满足条件次数达到parties之后,唤醒所有线程之前执行其 run()方法
    private final Runnable barrierCommand;
    // 当前周期
    private Generation generation = new Generation();
    // 剩余满足条件次数
    private int count;
    
    // ...
}

  Leer CyclicBarrierdespués de varias variables, mirar a sus detalles de implementación interna.

  Primer vistazo constructor , que tiene dos constructores, un despertador directamente después de alcanzar el número total de todos los temas (partidos) condición , otro designado una Runnableprimera ejecutar después de haber alcanzado sus condiciones método run total () y luego se despiertan.

  • No especifique Runnable, sólo un parámetro: la necesidad de alcanzar una serie de tareas
public CyclicBarrier(int parties) {
    // 直接调用另一个构造方法,Runnable传null,表示不执行
    this(parties, null);
}
  • Especifica el Runnableconstructor, el número total de asignación de tareas, el número de tareas pendientes, despierta antes de la operaciónRunnable
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    // 任务总数
    this.parties = parties;
    // 剩余需要完成的任务数
    this.count = parties;
    // 唤醒之前执行的Runnable
    this.barrierCommand = barrierAction;
}

  En la primera sección se utiliza el primer constructor, para tratar la segunda

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

    ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
    /** =======增加Runnable,其他地方保持一致=============*/
    CyclicBarrier barrier = new CyclicBarrier(3, ()-> System.err.println("在会议开始之前,先给大家发下开会资料"));

    executor.execute(() -> {
        try {
            System.err.println("张1到达会议室");
            barrier.await();
            System.err.println("会议开始,张1开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张2到达会议室");
            barrier.await();
            System.err.println("会议开始,张2开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张3先去个厕所,内急解决再去开会");
            TimeUnit.SECONDS.sleep(1);
            System.err.println("张3到达会议室");
            barrier.await();
            System.err.println("会议开始,张3开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });


    executor.shutdown();
}

Resultados Figura:

pic2

 La lectura del constructor, incluso si la mitad apreciado CyclicBarrier, la otra mitad de la próxima legislatura -; await()el código de seguimiento, esto es visto

public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}

Llame directamente dowaitmétodo para la participación de las masas falsey de 0 , lo que significa que espera ilimitada, a menos que el hilo se interrumpe o estela . Re-entrada dowaitmétodo, que es la CyclicBarrierotra mitad, en el siguiente código escrito claramente en todo el proceso de ejecución

/** 参数说明, timed:是否限时, nanos:限时时间*/
private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException, TimeoutException {
    // 锁
    final ReentrantLock lock = this.lock;
    // 获取锁,如果失败的话线程睡眠,进入同步队列(AQS中的知识)
    lock.lock();
    try {
        /* 拿到锁之后进入代码处理逻辑*/
        
        // 当前周期
        final Generation g = generation;

        // 如果当前周期是失败的,那么直接抛错
        if (g.broken)
            throw new BrokenBarrierException();

        // 如果当前线程被打断了,那么此次周期失败,设置相关参数,然后抛错
        if (Thread.interrupted()) {
            // 实现代码在下行的注释中,设置相关参数来提醒其他线程周期失败了
            breakBarrier();
            /*
             * private void breakBarrier() {
             *     generation.broken = true;
             *     count = parties;
             *     // 唤醒condition中的所有线程
             *     trip.signalAll();
             * }
             */
            throw new InterruptedException();
        }

        // 如果成功了,那么剩余任务数(count)减1
        int index = --count;
        // 如果为0则表示达到剩余的任务数没有了,达到CyclicBarrier的条件总数了,需要唤醒其他线程
        if (index == 0) {  
            boolean ranAction = false;
            try {
                // 唤醒之前的Runnable
                final Runnable command = barrierCommand;
                // 如果不为空的话执行其run方法
                if (command != null)
                    command.run();
                ranAction = true;
                // 开启下个周期,这个方法是CyclicBarrier可以复用的原因,具体实现在下行注释
                nextGeneration();
                /* private void nextGeneration() {
                 *     // 首先叫醒当前周期的其他线程,告诉其周期结束了,可以执行接下来的操作了
                 *     trip.signalAll();
                 *     // 然后开启下个周期,剩余任务数重置
                 *     count = parties;
                 *     // 下个周期
                 *     generation = new Generation();
                 * }
                 */
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // 如果还不能结束本周期,就一直等待直到结束或者周期失败
        for (;;) {
            try {
                // await的过程中是释放锁的
                // 不限时的话就一直等待直到被唤醒或者打断
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    // 否则的话等待一段时间后醒来
                    nanos = trip.awaitNanos(nanos);
            } 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)
                throw new BrokenBarrierException();

            if (g != generation)
                return index;

            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        // 释放锁
        lock.unlock();
    }
}

  Aquí, en la comprensión básica de CyclicBarrierla cuenta interna, al igual que otros argumentos awaites la misma lógica, pero es un acceso más limitado solamente.

  De hecho, si usted sabe ReentrantLock, entonces usted sabe que CyclicBarrierel todo está en ReentrantLockla conditionutilización de la misma.

3. Resumen

  En general, CyclicBarrierla aplicación es relativamente sencilla, dijo que ReentrantLockla conditionversión mejorada no es excesiva. Los puntos clave de dos, por un constructor, antes de decidir el número de tareas y operaciones de despertador ; Otro punto awaitmétodo, cada vez que en circunstancias normales awaitse reduciría un número de tareas ( el número total determinados por el constructor ), el número de tareas el tiempo se convierte en cero indica el final del ciclo , tienen que despertar conditionotros hilos, pero en el camino fallo de encuentro, entonces falla el ciclo actual, se despiertan otros hilos de error juntas de lanzamiento.



El fracaso no te hace débil, el miedo al fracaso será.

Supongo que te gusta

Origin www.cnblogs.com/zhangweicheng/p/12668921.html
Recomendado
Clasificación