2011-07-26 9 views
14

This question hace dos años, pero los recursos que menciona no son muy útiles (en mi humilde opinión) o los enlaces ya no son válidos.Java: se solicitaron tutoriales/explicaciones de jsr166y Phaser

Debe haber algunos buenos tutoriales para comprender Phaser. He leído el javadoc, pero mis ojos se vuelven vidriosos, ya que para entender realmente el javadoc debes saber cómo se supone que se usarán estas clases.

¿Alguien tiene alguna sugerencia?

+1

The Sun/Oracle [tutoriales] (http://download.oracle.com/javase/tutorial/essential/ concurrency/forkjoin.html) siempre son bastante buenos. – toto2

Respuesta

43

Para Phaser He respondido algunas preguntas. Verlos puede ayudar a comprender sus aplicaciones. Están vinculados en la parte inferior. Pero para comprender lo que hace el Phaser y por qué es útil, es importante saber qué soluciona.

Éstos son atributos de un CountdownLatch y CyclicBarrier

Nota:

  • Número de partes es otra forma de decir # de diferentes hilos
  • no reutilizable significa que usted tendrá que crear una nueva instancia de la barrera antes de reutilizar
  • Una barrera se puede avanzar si un hilo puede llegar y continuar haciendo el trabajo sin esperar a que otros o puedan esperar a que todo hilos para completar

CountdownLatch

  • Número fijo de partes
  • No resuable
  • puede avanzar (mira latch.countDown();hacerse avanzar latch.await();debe esperar)

CyclicBarrier

  • Número fijo de partes
  • reutilizables
  • no puede avanzar

Así que la CountdownLatch ¿no reutilizable, debe crear una nueva instancia cada vez , pero es realizable. CylicBarrier se puede reutilizar, pero todos los hilos deben esperar a que cada parte llegue a la barrera.

Phaser

  • número dinámico de partes
  • reutilizables
  • hacerse avanzar

Cuando un hilo quiere ser conocido a la Phaser invocan phaser.register() cuando llega el hilo en la barrera que invocan phaser.arrive()y aquí es donde se puede avanzar. Si el subproceso quiere esperar a que se completen todas las tareas registradas phaser.arriveAndAwaitAdvance()

También existe el concepto de una fase en la cual los subprocesos pueden esperar al finalizar otras operaciones que pueden no haberse completado. Una vez que todos los hilos llegan a la barrera del phaser se crea una nueva fase (un incremento de uno).

Puede echar un vistazo a mis otras respuestas, tal vez va a ayudar:

Java ExecutorService: awaitTermination of all recursively created tasks

Flexible CountDownLatch?

5

Para Phaser al menos, creo que el JavaDoc ofrece una explicación bastante clara. Esta es una clase que se usaría para sincronizar un lote de hilos, en el sentido de que su puede registrar cada hilo en el lote con un Phaser y luego usar el Phaser tenerlos bloquean hasta que cada hilo en el lote ha notificado a la Phaser, en ese punto cualquier hilo bloqueado comenzará a ejecutarse. Este ciclo de espera/notificación puede repetirse una y otra vez, según lo deseado/requerido.

Su código de ejemplo da un ejemplo razonable (aunque mucho me gusta su estilo de sangría de 2 caracteres):

void runTasks(List<Runnable> tasks) { 
    final Phaser phaser = new Phaser(1); // "1" to register self 
    // create and start threads 
    for (final Runnable task : tasks) { 
    phaser.register(); 
    new Thread() { 
     public void run() { 
     phaser.arriveAndAwaitAdvance(); // await all creation 
     task.run(); 
     } 
    }.start(); 
    } 

    // allow threads to start and deregister self 
    phaser.arriveAndDeregister(); 
} 

Esto establece una Phaser con un recuento de registro de tasks.size() + 1, y para cada tarea crea una nuevo Thread que bloqueará hasta que el siguiente avance de la Phaser (es decir, el tiempo en el que tasks.size() + 1 llegadas han sido registrados) y luego ejecutar su tarea asociada. Cada Thread que se crea también se inicia instantáneamente, por lo que Phaser sale del circuito con tasks.size() llegadas registradas.

La llamada final al phaser.arriveAndDeregister() registrará la llegada final, y también disminuirá el recuento de registro para que ahora sea igual a tasks.size(). Esto hace que el Phaser avance, lo que de hecho permite que todas las tareas comiencen a ejecutarse al mismo tiempo. Esto podría repetirse haciendo algo como:

void runTasks(List<Runnable> tasks) { 
    final Phaser phaser = new Phaser(1); // "1" to register self 
    // create and start threads 
    for (final Runnable task : tasks) { 
    phaser.register(); 
    new Thread() { 
     public void run() { 
     while (true) { 
      phaser.arriveAndAwaitAdvance(); // await all creation 
      task.run(); 
     } 
     } 
    }.start(); 
    } 

    // allow threads to start and deregister self 
    phaser.arriveAndDeregister(); 
} 

... esto es lo mismo que antes, excepto con la adición de un bucle que hace que la tarea se ejecute repetidamente. Debido a que cada iteración llama phaser.arriveAndAwaitAdvance() la ejecución de los hilos de tareas se sincronizará de forma que la tarea-0 no comienza su segunda iteración hasta cualquier otra tarea ha completado su primera iteración y notificado la Phaser que es está listo para comenzar su segunda iteración.

Esto puede ser útil si las tareas que está ejecutando varían mucho en la cantidad de tiempo que tardan en ejecutarse y si desea asegurarse de que los hilos más rápidos no se desincronicen con los más lentos.

Para una posible aplicación en el mundo real, considere un juego que ejecute gráficos separados y subprocesos de física. No desea tener los datos de computación del hilo de física para el fotograma 100 si el subproceso de gráficos está atascado en el fotograma 6, y usar un Phaser es un enfoque posible para garantizar que los gráficos y los subprocesos de física siempre funcionen en el mismo fotograma en el mismo tiempo (y también que si un hilo es significativamente más lento que el otro, el hilo más rápido genera con gracia recursos de CPU, de modo que, con suerte, el hilo más lento puede alcanzar más rápido).

+0

su 2da. Lista del código es igual que la 1ra ... –

+0

@Jason - No, no lo es, mire más de cerca. El segundo listado de código tiene un bucle 'while (true) {}' dentro del 'Thread', inmediatamente después de' public void run() '. – aroth

+0

ah. lo tengo. sin embargo, si está citando el javadoc, debe usar su código ya que manejan la terminación. –

1

Phaser es algo similar en funcionalidad de CyclicBarrier y CountDownLatch, pero proporciona más flexibilidad que los dos.

En CyclicBarrier solíamos registrando partes en el constructor, pero Phaser nos brinda la flexibilidad de registrar y eliminar partes en cualquier momento. Para la inscripción de partidos, podemos utilizar cualquiera de los siguientes -

  • constructores
  • registro, o
  • bulkRegister

Para cancelar el registro de partes, podemos utilizar -

  • arriveAndDeregister, o


Registro- agrega/Registra un nuevo partido unarrived a este sincronizador. Devuelve

  • el número de fase de llegada a la que se aplica este registro.
  • Si el phaser ha terminado, el valor es negativo y el registro no tiene efecto.

Si la invocación del método onAdvance() está en curso que antes de regresar, este método puede esperar su finalización. Si este phaser tiene un padre, y no hubo partes registradas con este phaser, este phaser hijo también está registrado con su padre.
Programa para demostrar el uso de Phaser>

import java.util.concurrent.Phaser; 

public class PhaserTest { 
public static void main(String[] args) { 

     /*Creates a new phaser with 1 registered unArrived parties 
     * and initial phase number is 0 
     */ 
     Phaser phaser=new Phaser(1); 
     System.out.println("new phaser with 1 registered unArrived parties" 
        + " created and initial phase number is 0 "); 

     //Create 3 threads 
     Thread thread1=new Thread(new MyRunnable(phaser,"first"),"Thread-1"); 
     Thread thread2=new Thread(new MyRunnable(phaser,"second"),"Thread-2"); 
     Thread thread3=new Thread(new MyRunnable(phaser,"third"),"Thread-3"); 

     System.out.println("\n--------Phaser has started---------------"); 
     //Start 3 threads 
     thread1.start(); 
     thread2.start(); 
     thread3.start(); 

     //get current phase 
     int currentPhase=phaser.getPhase(); 
     /*arriveAndAwaitAdvance() will cause thread to wait until current phase 
     * has been completed i.e. until all registered threads 
     * call arriveAndAwaitAdvance() 
     */ 
     phaser.arriveAndAwaitAdvance(); 
     System.out.println("------Phase-"+currentPhase+" has been COMPLETED----------"); 

     //------NEXT PHASE BEGINS------ 

     currentPhase=phaser.getPhase(); 
     phaser.arriveAndAwaitAdvance(); 
     System.out.println("------Phase-"+currentPhase+" has been COMPLETED----------"); 

     /* current thread Arrives and deRegisters from phaser. 
     * DeRegistration reduces the number of parties that may 
     * be required to advance in future phases. 
     */ 
     phaser.arriveAndDeregister(); 

     //check whether phaser has been terminated or not. 
     if(phaser.isTerminated()) 
       System.out.println("\nPhaser has been terminated"); 

} 
} 





class MyRunnable implements Runnable{ 

Phaser phaser; 

MyRunnable(Phaser phaser,String name){ 
     this.phaser=phaser; 
     this.phaser.register(); //Registers/Add a new unArrived party to this phaser. 
     System.out.println(name +" - New unarrived party has " 
        + "been registered with phaser"); 
} 

@Override 
public void run() { 
     System.out.println(Thread.currentThread().getName() + 
        " - party has arrived and is working in " 
        + "Phase-"+phaser.getPhase()); 
     phaser.arriveAndAwaitAdvance(); 

     //Sleep has been used for formatting output 
     try { 
       Thread.sleep(1000); 
     } catch (InterruptedException e) { 
       e.printStackTrace(); 
     } 

     //------NEXT PHASE BEGINS------ 

     System.out.println(Thread.currentThread().getName() + 
        " - party has arrived and is working in " 
        + "Phase-"+phaser.getPhase()); 
     phaser.arriveAndAwaitAdvance(); 

     phaser.arriveAndDeregister(); 
} 

}


bulkRegister - añade partes número de nuevos partidos unarrived a este sincronizador. Devuelve

  • el número de fase de llegada a la que se aplica este registro.
  • Si el phaser ha terminado, el valor es negativo y el registro no tiene efecto.

Si la invocación del método onAdvance() está en curso que antes de regresar, este método puede esperar su finalización.

arriveAndDeregister- Tema actual (Fiesta) Llega y se reinscribe de phaser. La baja en el registro reduce la cantidad de partes que pueden requerirse en el futuro para pasar a la siguiente fase.

Si este phaser tiene un padre, y no hubo partes registradas con este phaser, este phaser hijo también se registra con su padre. Programa para demostrar los padres y el niño Phaser

import java.util.concurrent.Phaser; 

public class PhaserParentChildTest { 
public static void main(String[] args) { 


    /* 
    * Creates a new phaser with no registered unArrived parties. 
    */ 
    Phaser parentPhaser = new Phaser(); 

    /* 
    * Creates a new phaser with the given parent & 
    * no registered unArrived parties. 
    */ 
    Phaser childPhaser = new Phaser(parentPhaser,0); 

    childPhaser.register(); 

    System.out.println("parentPhaser isTerminated : "+parentPhaser.isTerminated()); 
    System.out.println("childPhaser isTerminated : "+childPhaser.isTerminated()); 

    childPhaser.arriveAndDeregister(); 
    System.out.println("\n--childPhaser has called arriveAndDeregister()-- \n"); 

    System.out.println("parentPhaser isTerminated : "+parentPhaser.isTerminated()); 
    System.out.println("childPhaser isTerminated : "+childPhaser.isTerminated()); 

} 
} 

Leer más sobre Phaser en http://www.javamadesoeasy.com/2015/03/phaser-in-java_21.html