2015-12-14 15 views
7

Ich bin auf der Suche nach einem Ergebnis von einer Methode, die eine Weile dauern kann und das Objekt nicht wirklich zurückgibt, also möchte ich so effektiv wie möglich damit umgehen. Hier ist ein Beispiel dafür, was ich versuche zu erreichen:Ein Ergebnis in der Zukunft?

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? 
    } 

Verfahren callObject nennen das Objekt zu erhalten, jedoch eine andere Methode wird mit dem Objekt in genannt Ich möchte irgendeinemethode() in der Lage sein, zu nennen. für das Objekt, und dann zurückgeben, was es schließlich empfängt, obwohl der tatsächliche Aufruf und der Empfang separate Methoden sind.

Ich habe mit FutureTasks und Callables untersucht, die ich denke ist der Weg nach vorne, ich bin nur nicht sicher, wie man es implementieren.

Entschuldigung, wenn ich mich nicht so gut erklären würde, werde ich bei Bedarf weitere Informationen geben.

Danke!

Antwort

5

Sie könnten eine Methode schreiben, die eine lang andauernde Aufgabe asynchron ausführt. Sie würden dann ein zukünftiges Objekt zurückgeben, das leer ist, aber gefüllt wird, wenn die lang andauernde Aufgabe abgeschlossen ist. In anderen Programmiersprachen wird dies als Versprechen bezeichnet.

Hier ist ein einfaches Beispiel. Ich habe eine Methode namens someLongAsyncOperation erstellt, die etwas ausführt, das eine Weile dauert. Um dies zu simulieren, schlafe ich nur 3 Sekunden, bevor ich eine Antwort erzeuge.

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

Wenn Sie diese kleine Testklasse ausführen, werden Sie sehen, dass someLongAsyncOperation kehrt schnell, aber wenn future.get(); Aufruf warten wir auf den Vorgang abzuschließen.

Sie könnten jetzt etwas wie das Starten von mehr als einer longAsyncOperation tun, also würden sie parallel laufen. Und dann warte, bis alle fertig sind.

Funktioniert das als Ausgangspunkt für Sie?

EDIT

Sie someMethod wie dies implementieren könnte:

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

, die den Asynchron-Operation synchron wieder machen, indem sie sie anrufen und auf das Ergebnis warten.

EDIT2

Hier ist ein weiteres Beispiel, das warten verwendet/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 wartet, bis receivedObject existiert. receiveObject benachrichtigt bei Ankunft.

+0

Es ist ein guter Ausgangspunkt, aber ich bin nicht sicher, wie das Empfangsverfahren die someLongAsyncOperation mit seinem Ergebnis liefern würde? – Anonomoose

+0

Das klingt so, als ob 'receiveObject' aufgerufen werden soll, wenn der Vorgang beendet ist. Das ist eher ein Rückruf als eine Zukunft.Lassen Sie mich versuchen, ein Beispiel zu geben ... –

+0

Ja, das ist, was ich vorhabe - Entschuldigung! – Anonomoose

0

Sie benötigen einen Rückruf:

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

Sieht vielversprechend aus, wo käme der Empfang des Objekts dazu? – Anonomoose

+0

Sie erhalten es im Callback, Sie können es von dort anstelle von System.out aufrufen. Natürlich müssen Sie sicherstellen, dass der Callback auf einem Backthread ausgeführt wird und der Callback auf dem Hauptthread ausgeführt wird. Aber das hängt davon ab, welches Threading-System Sie verwenden möchten. – yedidyak

+0

Okay, aber wenn man sich auf das Ergebnis bezieht, woher weiß es, dass das Ergebnis empfangen wurde? – Anonomoose