Domanda

Ecco due blocchi di codice che realizzano (quello che penso sia) la stessa cosa.

In pratica sto cercando di imparare come usare la concorrenza di Java 1.5 per allontanarmi da Thread.sleep (long). Il primo esempio utilizza ReentrantLock e il secondo esempio utilizza CountDownLatch. Il jist di ciò che sto cercando di fare è mettere in pausa un thread fino a quando una condizione non viene risolta in un altro thread.

ReentrantLock fornisce un blocco sul valore booleano che sto usando per decidere se riattivare l'altro thread o meno, quindi uso la condizione con wait / signal per mettere in pausa l'altro thread. Per quanto ne so, l'unica ragione per cui avrei bisogno di usare i blocchi è se più di un thread richiedesse l'accesso in scrittura al booleano.

CountDownLatch sembra fornire la stessa funzionalità di ReentrantLock ma senza i blocchi (non necessari?). Tuttavia, mi sembra di dirottare il suo uso previsto inizializzandolo con un solo conto alla rovescia necessario. Penso che dovrebbe essere usato quando più thread lavoreranno sulla stessa attività, non quando più thread sono in attesa su una sola attività.

Quindi, domande:

  1. Sto usando i lucchetti per " la cosa giusta " nel codice ReentrantLock? Se sto scrivendo sul booleano solo in un thread, sono necessari i blocchi? Finché ripristino il valore booleano prima di svegliare qualsiasi altro thread, non posso causare un problema, posso?

  2. Esiste una classe simile a CountDownLatch che posso usare per evitare i blocchi (supponendo che dovrei evitarli in questo caso) che è più naturalmente adatta a questo compito?

  3. Esistono altri modi per migliorare questo codice di cui dovrei essere a conoscenza?

ESEMPIO 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;
}
}

ESEMPIO DUE:

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;
    }
}
È stato utile?

Soluzione

Entrambi gli approcci sono approssimativamente equivalenti, tranne per il fatto che a CountDownLatch può essere rilasciato solo una volta. Dopodiché tutti await () le chiamate ritornano all'istante. Quindi un CyclicBarrier potrebbe effettivamente essere più appropriato se stai lavorando con un servizio che può andare su e giù.

Se la tua condizione è davvero un affare one-shot, allora un FutureTask sarebbe più appropriato. Puoi chiamare get () che aspetterebbe che il servizio diventi disponibile, quindi puoi utilizzare il servizio non appena get () ritorna.

Si dice che CountDownLatch consente di attendere senza usare i blocchi. Tuttavia, sia CountDownLatch e ReentrantLock sono implementati utilizzando AbstractQueuedSynchronizer . Sotto il cofano, forniscono identica sincronizzazione e semantica di visibilità.

Altri suggerimenti

Secondo me, l'approccio della variabile lock / condition è migliore per questa attività. C'è un esempio simile al tuo qui: http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

In risposta alla protezione di un valore booleano. È possibile utilizzare volatile ( http://www.ibm.com/developerworks /java/library/j-jtp06197.html ). Tuttavia, il problema con il non utilizzo di Locks è che, a seconda della durata del servizio, continuerai a girare mentre is (isServiceDown ()). Utilizzando una condizione wait si dice al sistema operativo di mettere in pausa il thread fino a quando un risveglio spurio (di cui si parla nei documenti java per Condition) o fino a quando la condizione non viene segnalata nell'altro thread.

ESEMPIO DI ATTIVITÀ ASYNC BLOCCATO COMPLETO:

CON SCHEMA DI FLUSSO DEL CODICE:

inserisci qui la descrizione dell'immagine

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top