2015-12-14 25 views
7

Sto cercando di ottenere un risultato da un metodo che può richiedere del tempo per completare e in realtà non restituisce l'oggetto, quindi mi piacerebbe affrontarlo nel modo più efficace possibile. Ecco un esempio di quello che sto cercando di realizzare:Ottenere un risultato nel futuro?

public static void main (String[] args) { 
     Object obj = someMethod(); 

     System.out.println("The object is" + obj + ", wooh!"); 
    } 

    public void callObject() { 
     // Sends request for the object 
    } 

    public void receiveObject(Object object) { 
     // Received the object 
    } 

    public Object someMethod() { 
     callObject(); 
     // delay whilst the object is being received 
     // return received object once received, but how? 
    } 

Il metodo callObject chiamerà per ottenere l'oggetto, ma un diverso metodo viene chiamato con l'oggetto in Voglio someMethod() per essere in grado di chiamare. per l'oggetto e quindi restituire ciò che alla fine riceve, anche se l'effettiva chiamata e ricezione sono metodi separati.

Ho esaminato FutureTasks e Callables che I penso che sia la via da seguire, non sono proprio sicuro di come implementarlo.

Scusate se non mi sono spiegato troppo bene, darò maggiori informazioni se necessario.

Grazie!

risposta

5

È possibile scrivere un metodo, che esegue alcuni task di lunga durata in modo asincrono. Dovresti quindi restituire un oggetto futuro, che è vuoto ma viene riempito quando l'attività di lunga durata è completata. In altri linguaggi di programmazione, questo è chiamato una promessa.

Ecco un semplice esempio. Ho creato un metodo chiamato someLongAsyncOperation che esegue qualcosa che richiede un po 'di tempo. Per simulare questo, dormo solo per 3 secondi prima di generare una risposta.

import java.util.UUID; 
import java.util.concurrent.*; 

public class Test { 

    private static final ExecutorService executorService = Executors.newSingleThreadExecutor(); 

    public Future<MyAnswer> someLongAsyncOperation(){ 

     Future<MyAnswer> future = executorService.submit(() -> { 
      Thread.sleep(3000); 
      return new MyAnswer(UUID.randomUUID().toString()); 
     }); 

     return future; 
    } 


    public static void main(String[] args) throws Exception { 

     System.out.println("calling someLongAsyncOperation ..."); 
     Future<MyAnswer> future = new Test().someLongAsyncOperation(); 
     System.out.println("calling someLongAsyncOperation done."); 

     // do something else 

     System.out.println("wait for answer ..."); 
     MyAnswer myAnswer = future.get(); 
     System.out.printf("wait for answer done. Answer is: %s", myAnswer.value); 

     executorService.shutdown(); 
    } 

    static class MyAnswer { 
     final String value; 

     MyAnswer(String value) { 
      this.value = value; 
     } 
    } 
    } 

Se si esegue questa piccola classe di test, vedrete, che someLongAsyncOperation rendimenti veloci, ma quando si chiama future.get(); aspettiamo il completamento dell'operazione.

Ora è possibile eseguire qualcosa come l'avvio di più di un AsyncOperazione lunga, in modo che vengano eseguiti in parallelo. E poi aspetta che siano finiti tutti.

Questo funziona come punto di partenza per voi?

EDIT

Si potrebbe implementare someMethod come questo:

public MyAnswer someMethod() throws ExecutionException, InterruptedException { 
     Future<MyAnswer> future = someLongAsyncOperation(); // kick of async operation 
     return future.get(); // wait for result 
    } 

che renderà di nuovo l'operazione asincrona synchron, chiamandolo e in attesa del risultato.

EDIT2

Ecco un altro esempio che utilizza l'ora/notify:

import java.util.UUID; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 
import java.util.concurrent.locks.Condition; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 

public class Test2 { 

    private static final ExecutorService executorService = Executors.newSingleThreadExecutor(); 
    private Object receivedObject; 
    private final Object mutex = new Object(); 

    public static void main (String[] args) throws InterruptedException { 
     Object obj = new Test2().someMethod(); 

     System.out.println("The object is" + obj + ", wooh!"); 

     executorService.shutdown(); 
    } 

    public void callObject() { 

     System.out.println("callObject ..."); 

     // Sends request for the object asynchronously! 
     executorService.submit(() -> { 

      // some wait time to simulate slow request 
      try { 
       Thread.sleep(3000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

      // provide object to callback 
      receiveObject(UUID.randomUUID().toString()); 
     }); 

     System.out.println("callObject done."); 
    } 

    public void receiveObject(Object object) { 

     System.out.println("receiveObject ..."); 

     synchronized (mutex) { 
      this.receivedObject = object; 
      mutex.notify(); 
     } 

     System.out.println("receiveObject done."); 
    } 

    public Object someMethod() throws InterruptedException { 

     System.out.println("someMethod ..."); 

     synchronized (mutex) { 
      callObject(); 
      while(this.receivedObject == null){ 
       mutex.wait(); 
      } 
     } 

     System.out.println("someMethod done."); 
     return this.receivedObject; 
    } 

} 

someMethod attende fino receivedObject esiste. receiveObject notifica all'arrivo.

+0

E 'un buon punto di partenza, ma sono sicuro su come il metodo di ricezione fornirebbe la someLongAsyncOperation con il suo risultato? – Anonomoose

+0

Sembra che tu voglia ricevere "receiveObject" quando l'operazione è terminata. È più come una richiamata, piuttosto che un futuro.Fammi provare a fornire un esempio ... –

+0

Sì, questo è quello che sto andando - scusa! – Anonomoose

0

Hai bisogno di un callback:

private abstract class Callback<T>{ 
    run(T object); 
} 


public Object someMethod() { 
    callObject(new Callback<Object>() 
    { 
     @Override 
     public void run(Object object) 
     { 
      System.out.println("The object is" + object + ", wooh!"); 
     } 
    }) 

} 

public void callObject(Callback<Object> callback) { 
    // Sends request for the object 
    callback.run(object); 
} 
+0

Sembra promettente, da dove arriverà la ricezione dell'oggetto? – Anonomoose

+0

Si ottiene nel callback, è possibile chiamarlo da lì anziché System.out. Naturalmente, è necessario assicurarsi che il callback sia eseguito su un thread precedente e che la callback sia eseguita sul thread principale. Ma ciò dipende dal sistema di threading che si desidera utilizzare. – yedidyak

+0

Ok, ma facendo riferimento al risultato, come fa a sapere che il risultato è stato ricevuto? – Anonomoose

Problemi correlati