Pregunta

Aquí hay dos fragmentos de código que logran (lo que creo que es) lo mismo.

Básicamente estoy tratando de aprender cómo usar la concurrencia de Java 1.5 para alejarme de Thread.sleep (largo). El primer ejemplo usa ReentrantLock, y el segundo ejemplo usa CountDownLatch. La esencia de lo que estoy tratando de hacer es poner un hilo a dormir hasta que se resuelva una condición en otro hilo.

El ReentrantLock proporciona un bloqueo en el booleano que estoy usando para decidir si activar el otro hilo o no, y luego uso la Condición con esperar / señal para dormir el otro hilo. Por lo que puedo decir, la única razón por la que necesitaría usar bloqueos es si más de un hilo requiere acceso de escritura al booleano.

El CountDownLatch parece proporcionar la misma funcionalidad que ReentrantLock pero sin los bloqueos (¿innecesarios?). Sin embargo, parece que estoy secuestrando su uso previsto al inicializarlo con solo una cuenta regresiva necesaria. Creo que se supone que debe usarse cuando varios subprocesos van a funcionar en la misma tarea, no cuando varios subprocesos están esperando una tarea.

Entonces, preguntas:

  1. ¿Estoy usando bloqueos para "lo correcto"? en el código ReentrantLock? Si solo escribo en el booleano en un hilo, ¿son necesarios los bloqueos? Mientras restablezca el valor booleano antes de activar cualquier otro subproceso, no puedo causar un problema, ¿puedo?

  2. ¿Existe una clase similar a CountDownLatch que pueda usar para evitar bloqueos (suponiendo que debería evitarlos en este caso) que sea más natural para esta tarea?

  3. ¿Hay otras formas de mejorar este código que debería conocer?

EJEMPLO UNO:

import java.util.concurrent.locks.*;

public class ReentrantLockExample extends Thread {

//boolean - Is the service down?
boolean serviceDown;

// I am using this lock to synchronize access to sDown
Lock serviceLock; 
// and this condition to sleep any threads waiting on the service.
Condition serviceCondition;

public static void main(String[] args) {
    Lock l = new ReentrantLock();
    Condition c = l.newCondition(); 
    ReentrantLockExample rle = new ReentrantLockExample(l, c);

    //Imagine this thread figures out the service is down
    l.lock();
    try {
        rle.serviceDown = true;
    } finally {
        l.unlock();
    }

    int waitTime = (int) (Math.random() * 5000);
    System.out.println("From main: wait time is " + waitTime);
    rle.start();
    try {
        //Symbolizes some random time that the service takes to come back up.
        Thread.sleep(waitTime);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //Imagine this thread figures out that the service is back up.
    l.lock();
    try {
        rle.serviceDown = false;
        c.signal();
    } finally {
        l.unlock();
    }

}

//Constructor
public ReentrantLockExample(Lock l, Condition c) {  
    this.serviceLock = l;
    this.serviceCondition = c; 
}

/*
 * Should wait for this imaginary service to come back online.
 */
public void run() {
    System.out.println("Thread: start awaiting");
    serviceLock.lock();
    try {
        while (isServiceDown())
        {           
            serviceCondition.await();
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        serviceLock.unlock();
    }
    System.out.println("Thread: done awaiting");
}


private boolean isServiceDown() {       
    return serviceDown;
}
}

EJEMPLO DOS:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;

public class CountDownLatchExample extends Thread {

    //boolean - Is the service down?
    boolean serviceDown;

    // I am using this latch to wait on the service.
    CountDownLatch serviceLatch; 


    public static void main(String[] args) {
        CountDownLatch cdl = new CountDownLatch(1);     
        CountDownLatchExample cdle = new CountDownLatchExample(cdl);

        //Service goes down.
        cdle.serviceDown = true;        

        int waitTime = (int) (Math.random() * 5000);
        System.out.println("From main: wait time is " + waitTime);
        cdle.start();
        try {
            //Symbolizes some random time that the service takes to come back up.
            Thread.sleep(waitTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Service comes back up.
        cdle.serviceDown = false;
        cdl.countDown();    
    }

    //Constructor 
    public CountDownLatchExample(CountDownLatch cdl) {  
        this.serviceLatch = cdl;         
    }

    /*
     * Should wait for this imaginary service to come back online.
     */
    public void run() {
        System.out.println("Thread: start awaiting");
        try {
            while (isServiceDown()) {           
                serviceLatch.await();
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Thread: done awaiting");
    }

    private boolean isServiceDown() {       
        return serviceDown;
    }
}
¿Fue útil?

Solución

Cualquiera de los enfoques es más o menos equivalente, excepto que a CountDownLatch solo se puede lanzar una vez. Después de eso, todos aguardan () las llamadas regresan al instante. Entonces un CyclicBarrier puede ser más apropiado si está trabajando con un servicio que puede subir y bajar.

Si su condición es realmente un trato único, entonces un FutureTask sería más apropiado. Puede llamar a get () que esperaría a que el servicio esté disponible, y luego podría usar el servicio tan pronto como get () regrese.

Usted menciona que CountDownLatch permite esperar sin usar bloqueos. Sin embargo, CountDownLatch y ReentrantLock se implementan utilizando AbstractQueuedSynchronizer . Debajo del capó, proporcionan una semántica idéntica de sincronización y visibilidad.

Otros consejos

El enfoque de bloqueo / condición variable es mejor para esta tarea en mi opinión. Aquí hay un ejemplo similar al suyo: http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

En respuesta a la protección de un booleano. Puede usar volátil ( http://www.ibm.com/developerworks /java/library/j-jtp06197.html ). Sin embargo, el problema de no usar Bloqueos es que, dependiendo de cuánto tiempo esté inactivo su servicio, estará girando mientras (isServiceDown ()). Al usar una condición de espera, le dice al sistema operativo que suspenda su hilo hasta que se despierte de manera espuria (se menciona en los documentos de Java para Condición), o hasta que la condición se señale en el otro hilo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top