CyclicBarrier
Hay 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 CyclicBarrier
la 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 CyclicBarrier
puede 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:
a través de la parte superior del código puede conocer CyclicBarrier
algunos puntos:
- Utilizar
await()
para indicar la finalización de ciertas cosas. (Ejemplo anterior rendimiento llegó a la sala de conferencias ) - Uso
await()
después de la hebra actual entra en el estado bloqueado , necesidad de esperar a satisfacer plenamenteCyclicBarrier
las condiciones de la estela para continuar con la siguiente operación. (En el ejemplo anterior tres individuos han llegado a la sala de conferencias ) - 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 CyclicBarrier
usando 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
- constructor
- await método ()
Mientras estos dos métodos para comprender la interna, que es equivalente a entender el CyclicBarrier
interior.
Antes de ahondar en que, en primer vistazo a CyclicBarrier
varias 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 laReentrantLock
instancia, acercaReentrantLock
claro lata -> transferencia.viaje:
lock
encondition
,CyclicBarrier
utilizar la variable de lograr el bloqueo y despertar al mismo tiempo entre los hilos . Del mismo modo, hay una claracondition
acción => Portal .partes: la necesidad de satisfacer las condiciones (llaman
await
el número de formas), y es entonces cuando hay partidos esperan hilos () se despierta todos los hilos.barrierCommand: una
Runnable
variable en elawait
número total de llamadas al método para llegarparties
después, antes del despertar todos los hilos ejecutar surun()
métodogeneració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
await
mé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 CyclicBarrier
despué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 Runnable
primera 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
Runnable
constructor, 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:
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 dowait
método para la participación de las masas false
y de 0 , lo que significa que espera ilimitada, a menos que el hilo se interrumpe o estela . Re-entrada dowait
método, que es la CyclicBarrier
otra 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 CyclicBarrier
la cuenta interna, al igual que otros argumentos await
es la misma lógica, pero es un acceso más limitado solamente.
De hecho, si usted sabe ReentrantLock
, entonces usted sabe que CyclicBarrier
el todo está en ReentrantLock
la condition
utilización de la misma.
3. Resumen
En general, CyclicBarrier
la aplicación es relativamente sencilla, dijo que ReentrantLock
la condition
versió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 await
método, cada vez que en circunstancias normales await
se 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 condition
otros 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á.