Comprensión profunda de CyclicBarrie

Comprensión profunda de CyclicBarrier

Otros puntos de conocimiento


Comprensión profunda de los conceptos básicos de subprocesos múltiples de Java Comprensión profunda del
uso de aqs ReentrantLock
Comprensión profunda del semáforo Semáforo
Comprensión profunda de las tres características principales
de la concurrencia Comprensión profunda de la programación concurrente Comprensión profunda de CAS
En profundidad comprensión del conjunto de subprocesos Java CountDownLatch

Uso detallado de CyclicBarrier

Uso detallado de CyclicBarrier

Principio de Barrera Cíclica

Ejemplo de depuración para recorrer el proceso

El hilo del jugador t1 t2 y el hilo principal del árbitro están listos para el juego.


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


       CyclicBarrier cyclicBarrier = new CyclicBarrier(3);


        new Thread(() -> {
            try {
                Thread.sleep(10_000);
                System.out.println("t1 在准备 ");
                cyclicBarrier.await();   // 等另外一个一个线程准备好 然后开始做事情
                System.out.println("t1 准备好了  ");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }

        },"t1").start();

        new Thread(() -> {
            try {
                Thread.sleep(5_000);
                System.out.println("t2 在准备  ");
                cyclicBarrier.await(); // 等另外一个一个线程准备好 然后开始做事情
                System.out.println("t2 准备好了  ");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        },"t2"  ).start();

        System.out.println(" 裁判 在准备 ");
        cyclicBarrier.await();
        System.out.println(" 裁判 准备好了 ");


        while (true){
            if (cyclicBarrier.getNumberWaiting()==0){
                System.out.println(" 时间到: 开始比赛");
                return;
            }
        }


    }
复制代码

resultado de la operación

inserte la descripción de la imagen aquí

debug ejecuta el hilo principal primero

inserte la descripción de la imagen aquí
sin tiempo de espera
inserte la descripción de la imagen aquí

método específicoprivate int dowait(boolean timed, long nanos)

Puede ver que ReentrantLockel bloqueo , y el conteo se reduce en uno

inserte la descripción de la imagen aquí

Bloquear el hilo principal

Aquí el viaje es una cola condicional

private final Condition trip = lock.newCondition();
inserte la descripción de la imagen aquí

Puede ver que se crea un nodo de nodo condicional aquí

inserte la descripción de la imagen aquí

método addConditionWaiter
inserte la descripción de la imagen aquí
y luego LockSupport.park(this); bloquea el hilo actual

Puedes ver que el hilo principal ya está esperando, ahora vamos al hilo t1

inserte la descripción de la imagen aquí

Continúe siguiendo la lógica de cuenta regresiva anterior, en este momento el conteo aún no es igual a 0

inserte la descripción de la imagen aquí

Todavía tomará el método de cola condicional de espera

inserte la descripción de la imagen aquí

Crear un nuevo nodo de condición de nodo de subproceso y ponerlo en cola

inserte la descripción de la imagen aquí
luego sigue estacionando
inserte la descripción de la imagen aquí

Próximo hilo t2 en espera

inserte la descripción de la imagen aquí
Se encontrará que el recuento de subprocesos t2 es 0 en este momento, y se ejecuta el método nextGeneration();inserte la descripción de la imagen aquí

¿El subproceso t2 actual ejecuta el método signalAll para notificar a todos?

cuenta = fiestas; poner a cero el contador
inserte la descripción de la imagen aquí

Introduzca el método, obtenga el nodo principal,

inserte la descripción de la imagen aquí
Ejecute el método doSignalAll (primero)
para obtener el nodo principal y establezca el siguiente puntero en el
inserte la descripción de la imagen aquí
método nulo transferForSignal

Se encuentra que el estado del nodo condicional se establece en 0 primero, y luego se crea un nuevo nodo NodoNode p = enq(node);

inserte la descripción de la imagen aquí
Nuevo nodo nodo

这段代码很熟悉,是aqs里构建同步队列以及入队的方法,可以明白t2线程将条件队列里所有的节点转为同步队列

Nodo p = enq(nodo);

t2 最后执行unlock

inserte la descripción de la imagen aquí

释放锁
inserte la descripción de la imagen aquí
这里是也是aqs里的释放锁 逻辑,同时将获取锁的线程置为null

inserte la descripción de la imagen aquí
唤醒其他线程

inserte la descripción de la imagen aquí

将 Node 节点状态置为 0

inserte la descripción de la imagen aquí

唤醒main线程

inserte la descripción de la imagen aquí

此时main线程被t2唤醒

inserte la descripción de la imagen aquí

同理main线程这里也执行置为当前线程锁的线程未null

inserte la descripción de la imagen aquí
然后unLock

inserte la descripción de la imagen aquí
继续唤醒其他线层 这里是t1

inserte la descripción de la imagen aquí
这里还是这个方法,和上面逻辑一样 也是aqs里的

inserte la descripción de la imagen aquí

可以看到t1被唤醒,此时三个线程都被是唤醒状态

inserte la descripción de la imagen aquí

此时将main线程执行到这里

inserte la descripción de la imagen aquí
将t1执行到这里

inserte la descripción de la imagen aquí

将t2执行到这里

inserte la descripción de la imagen aquí

最后
CyclicBarrier 构造,count是计数器, parties 是备份的计数器,barrierCommand 是传进来的任务
inserte la descripción de la imagen aquí

在 await 方法里 dowait 时候可以看到 计数器是 0 了 可以执行这个方法,可以用来做执行结束通知

inserte la descripción de la imagen aquí

最后看重置方法

inserte la descripción de la imagen aquí

重置,以及通知
inserte la descripción de la imagen aquí

最后

实现原理

CyclicBarrier是通过ReentrantLock的"独占锁"和Conditon来实现一组线程的阻塞 唤醒的,而CountDownLatch则是通过AQS的“共享锁”实现

CyclicBarrier执行流程

大概范围的一个执行流程,没有那么细。

  • 1 await() 先进行计数逻辑,然后将线程node节点放入条件队列
  • 2 最后一个await() 计数为 0 时,该线程将条件队列转换为同步队列,然后唤醒下一个同步队列节点,也就是去唤醒下一个线程
  • 3 其他线程被唤醒继续唤醒下一个线程,这里的流程和之前aqs流程是一样的,都是aqs里的逻辑。

都是在lock加锁和解锁里进行操作的

CyclicBarrier与CountDownLatch的区别

  • 1 El contador de CountDownLatch solo se puede usar una vez, mientras que el contador de CyclicBarrier se puede restablecer con el método reset(). Por lo tanto, CyclicBarrier puede manejar escenarios comerciales más complejos, como si ocurre un error de cálculo, puede restablecer el contador y dejar que los subprocesos lo ejecuten nuevamente.
  • 2 CyclicBarrier también proporciona getNumberWaiting (puede obtener el número de hilos bloqueados por CyclicBarrier), isBroken (usado para saber si el hilo bloqueado está interrumpido) y otros métodos.
  • 3 CountDownLatch bloqueará el subproceso principal y CyclicBarrier no bloqueará el subproceso principal, solo bloqueará los subprocesos.
  • 4 Tanto CountDownLatch como CyclicBarrier pueden realizar la espera entre subprocesos, pero tienen énfasis diferentes. CountDownLatch generalmente se usa para uno o más subprocesos, esperando que otros subprocesos completen la tarea antes de ejecutar. CyclicBarrier generalmente se usa para que un grupo de subprocesos se esperen unos a otros hasta cierto estado, y luego este grupo de subprocesos se ejecuta al mismo tiempo.
  • 5 CyclicBarrier también puede proporcionar una barrierAction para combinar resultados de cálculos de subprocesos múltiples.
  • 6 CyclicBarrier usa el "bloqueo exclusivo" de ReentrantLock y Conditon para realizar el bloqueo de activación de un grupo de subprocesos, mientras que CountDownLatch se implementa a través del "bloqueo compartido" de AQS.

pestillo de cuenta regresiva

Comprensión profunda de CountDownLatch

Supongo que te gusta

Origin juejin.im/post/7231804086382084151
Recomendado
Clasificación