Conocimiento profundo de CyclicBarrier

Introducción

A partir de jdk1.5, se introdujo una clase java.util.concurrent.CyclicBarrier para controlar que varios subprocesos se esperen unos a otros. Estos subprocesos continuarán ejecutándose solo cuando lleguen varios subprocesos. A diferencia de CountDownLatch , CyclicBarrier casos puede ser reutilizada, también conocido como cercas repetidas

utilizar


class CyclicBarrierAgainDemo implements Runnable{
    
    
   @Override
   public void run() {
    
    
       System.out.print("------------");
   }
	public static void main(String[] args) {
    
    
	     int totalThread = 10;
	     CyclicBarrier cyclicBarrier = new CyclicBarrier(totalThread,new CyclicBarrierAgainDemo ());
	     ExecutorService executorService = Executors.newCachedThreadPool();
	     for (int i = 0; i < totalThread; i++) {
    
    
	         executorService.execute(() -> {
    
    
	             System.out.print("before..");
	             try {
    
    
	                 cyclicBarrier.await();
	             } catch (InterruptedException | BrokenBarrierException e) {
    
    
	                 e.printStackTrace();
	             }
	         });
	     }
	     executorService.shutdown();
	 }
}

Los subprocesos que utilizan CyclicBarrier para realizar la espera se denominan participantes. Los participantes solo necesitan ejecutarcyclicBarrier.await ()Puedes esperar. Dado que CyclicBarrier mantiene un bloqueo de pantalla internamente, es posible saber quién de los participantes es el último en ejecutarcyclicBarrier.await (). Cuando se ejecuta el último hilo, otros participantes que usan la instancia CyclicBarrier correspondiente se despertarán y el último hilo en sí no se suspenderá. El diagrama de flujo es el siguiente:
Inserte la descripción de la imagen aquí

Análisis de código fuente CyclicBarrier

Nota: Comentaré la función de cada método en el código fuente, puede consultar los comentarios para comprenderlo.

Primero mire el método de construcción de la clase CyclicBarrier:


public CyclicBarrier(int parties) {
    
    
        this(parties, null);
    }
    
public CyclicBarrier(int parties, Runnable barrierAction) {
    
    
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;//parties 指示计数器的初始值
        this.count = parties;
        this.barrierCommand = barrierAction;//所以线程到达屏障后会执行一次
    }

Entre ellos, el segundo método constructor es el más utilizado.

esperar()

public int await() throws InterruptedException, BrokenBarrierException {
    
    
        try {
    
    
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
    
    
            throw new Error(toe); // cannot happen
        }
    }
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;
            //最后一个线程到达栅栏
            if (index == 0) {
    
      // tripped
                boolean ranAction = false;
                try {
    
    
                	//栅栏被打破市执行的方法
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    //开启下一代,again
                    nextGeneration();
                    return 0;
                } finally {
    
    
                	//确保其他线程都够正常运行
                    if (!ranAction)
                        breakBarrier();
                }
            }
            //自旋
            for (;;) {
    
    
                try {
    
    
                	//判断时候有等待时间限制
                    if (!timed)
                    	//还是使用的Condition
                        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();
                    }
                }
				//如果线程因为打翻栅栏操作而被唤醒则抛出异常
                if (g.broken)
                    throw new BrokenBarrierException();
				如果线程因为换代操作而被唤醒则返回计数器的值
                if (g != generation)
                    return index;
				//等待时间已到,唤醒线程并抛出异常
                if (timed && nanos <= 0L) {
    
    
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
    
    
            lock.unlock();
        }
    }
  1. Obtenga el bloqueo de pantalla, determine si el estado actual del hilo se interrumpe, si es así, ejecutebreakBarrier Método para reactivar todos los hilos que estaban bloqueados antes y restablecer el contador
  2. La cuenta del contador se reduce en 1. Si cuenta == 0, significa que el último hilo llega a la cerca, y luego ejecuta la interfaz Runnable previamente especificada, y la ejecuta al mismo tiempo próxima generaciónAproximación a la próxima generación
  3. De lo contrario, ingrese el giro y determine si el hilo actual entra en espera programada o espera no programada. Si se interrumpe durante la espera, ejecutebreakBarrier Método para despertar todos los hilos que estaban bloqueados antes
  4. Determinar si es por ejecuciónbreakBarrierEl método se despierta, si lo está, se lanza una excepción
  5. Determinar si es una operación de reposición normal y despertado, de ser así, devolver el valor del contador
  6. Determine si se despertó por el tiempo de espera, si es así, despierte todos los subprocesos que estaban bloqueados antes y genere una excepción
  7. Liberando el bloqueo
    Podemos ver que el método de bloqueo usado aquí usa el método await () / awaitNanos (long time) de la variable de condición Condition . Hemos analizado cuidadosamente este método antes cuando pusimos la Condition . No lo analizaremos aquí. Los estudiantes interesados ​​pueden ir al enlace del artículo anterior: Comprensión profunda de la variable de condición Condición

breakBarrier ()

private void breakBarrier() {
    
    
        generation.broken = true;//栅栏被打破
        count = parties;//重置count
        trip.signalAll();//唤醒之前阻塞的线程
    }

próxima generación()

private void nextGeneration() {
    
    
        //唤醒所以的线程
        trip.signalAll();
        //重置计数器
        count = parties;
        //重新开始
        generation = new Generation();
    }

Reiniciar()

A continuación, observe el método de restablecimiento de la cerca

public void reset() {
    
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
    
    
            lock.unlock();
        }
    }

La diferencia entre CyclicBarrier y CountDownLatch

  1. CountDownLatch solo puede interceptar una ronda, mientras que CyclicBarrier puede lograr la intercepción cíclica.
  2. El contador de CyclicBarrier se controla por sí mismo, mientras que el contador de CountDownLatch lo pasa el usuariocuenta regresiva() Método para controlar

para resumir

Si el código es correctocyclicBarrier.await ()La llamada no se coloca en un bucle y el propósito de usar CyclicBarrier no es probar operaciones concurrentes altas, entonces el uso de CyclicBarrier en este momento puede ser un abuso

Supongo que te gusta

Origin blog.csdn.net/xzw12138/article/details/106588912
Recomendado
Clasificación