11

I'm looking to get a result from a method which can take a while to complete and doesn't actually return the object, so I'd like to deal with it as effectively as possible. Here's an example of what I'm trying to achieve:

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

The method callObject will call to get the object, however a different method is called with the object in. I want someMethod() to be able to call for the object, and then return what it eventually receives, even though the actual call and receive are separate methods.

I've looked into using FutureTasks and Callables which I think is the way forward, I'm just not too sure how to implement it.

Sorry if I didn't explain myself too well, I'll give more information if necessary.

Thanks!

Anonomoose
  • 137
  • 1
  • 1
  • 8

3 Answers3

12

You could write a method, that kicks of some long running task asynchronously. You would then return a future object, that is empty but gets filled when the long running task is completed. In other programming languages, this is called a promise.

Here is an simple example. I created a method called someLongAsyncOperation which executes something that takes a while. To simulate this, I just sleep for 3 seconds before generating an answer.

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

If you execute this little test class, you'll see, that someLongAsyncOperation returns fast, but when calling future.get(); we wait for the operation to complete.

You could now do something like starting of more than one longAsyncOperation, so they would run in parallel. And then wait until all of them are done.

Does this work as a starting point for you?

EDIT

You could implement someMethod like this:

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

Which will make the async operation synchron again, by calling it and waiting for the result.

EDIT2

Here's another example that uses wait/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 waits until receivedObject exists. receiveObject notifies upon arrival.

Tim Büthe
  • 62,884
  • 17
  • 92
  • 129
  • It's a good starting point, but I'm unsure on how the receiving method would provide the someLongAsyncOperation with its result? – Anonomoose Dec 14 '15 at 14:49
  • That sounds like you want `receiveObject` to be called when the operation has finished. That's more like a callback, instead of a future. Let me try to provide an example... – Tim Büthe Dec 14 '15 at 14:59
  • Yeah that's what I'm going for - sorry! – Anonomoose Dec 14 '15 at 15:02
  • One thing must be clear though, using future or callback mechanisms only makes sense, if you do work asynchronously. If `someMethod` just waits until this async work is done, what the point in doing it async in the first place? – Tim Büthe Dec 14 '15 at 15:09
  • There isn't too much of a focus on it having to be async or not (I'm not too sure which would be best), but the idea is that I can reference the result immediately after calling it without the rest of the code executing. Object result = getResult(); // Takes time to execute and get result System.out.println(result); – Anonomoose Dec 14 '15 at 15:12
  • I see where your edit is going, but the method calling the object doesn't return it, only triggers the result in a separate method which is the problem. So, is it possible to get the result using FutureTasks? – Anonomoose Dec 14 '15 at 15:24
  • @Anonomoose maybe wait/notify is a better fit. Check my edit. – Tim Büthe Dec 14 '15 at 16:36
  • Implemented your edit into my program, but it seems to be getting stuck on wait and then crashes. Any idea? – Anonomoose Dec 14 '15 at 23:33
  • What is the exception? – Tim Büthe Dec 14 '15 at 23:35
  • There doesn't seem to be one, but the dump given shows that the main thread is waiting. I did some tests, and the raw implementation you gave me runs on two different threads, whereas mine runs on just one. I'm assuming this is the issue, as when it waits it stops entirely, due to just being on one thread? – Anonomoose Dec 15 '15 at 00:01
0

You need a 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);
}
yedidyak
  • 1,964
  • 13
  • 26
  • Looks promising, where would the receiving of the object come into that though? – Anonomoose Dec 14 '15 at 14:46
  • You get it in the callback, you can call it from there instead of the System.out. Of course, you need to make sure that the callback is executed on a back thread, and the callback is executed on the main thread. But that depends on what threading system you want to use. – yedidyak Dec 14 '15 at 14:49
  • Okay, but by making a reference to the result, how does it know that the result has been received? – Anonomoose Dec 14 '15 at 14:51
  • You only call the callback when it has been. Then the callback takes it as a parameter. – yedidyak Dec 14 '15 at 14:52
  • I see, and which method calls the callback? – Anonomoose Dec 14 '15 at 14:57
  • In the example code I wrote, callObject calls it. But it should be wrapped to run on the main thread, after callObject is run on a back thread to make it asynchronous. – yedidyak Dec 14 '15 at 15:00
  • Okay, so how would I go about creating/storing that callback used in callObject() so that it can be ran when the object's received? – Anonomoose Dec 14 '15 at 15:09
  • You pass it as a parameter, like in the example I gave. – yedidyak Dec 14 '15 at 15:10
  • But where is that Callback created in the first place for it then to be referenced? – Anonomoose Dec 14 '15 at 15:14
  • In someMethod, as an anonymous inner class. – yedidyak Dec 14 '15 at 15:15
  • Gotcha, going back to my original question though - I can't see where the method called when the object is received comes into this? – Anonomoose Dec 14 '15 at 15:22
  • Instead of the line 'System.out.println("The object is" + object + ", wooh!");', put 'receiveObject(object);' – yedidyak Dec 15 '15 at 09:45
  • Like I said in the original question, the receiveMethod is called and out of my control (API), so I need to connect that receiving with the callback. – Anonomoose Dec 15 '15 at 19:25
0
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class ThreadExample implements Callable<String>{

    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        return "Ashish";
    }

}
public class FutureThreadExample {

    public static void main(String a[]) throws InterruptedException, ExecutionException {
        ExecutorService executorService=Executors.newFixedThreadPool(1);
        List <Future<String>>objList=new ArrayList<Future<String>>();
       for(int i=0;i<10;i++) {
            Future<String> obj=executorService.submit(new ThreadExample());
            objList.add(obj);
       }
        for( Future<String> fut:objList) {
            System.out.println(fut.get());
        }
        executorService.shutdown();
    }


}
Ashish Yadav
  • 7
  • 1
  • 1
  • 6
  • 2
    While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please [include an explanation for your code](https://meta.stackexchange.com/q/114762/269535), as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. You can use the [edit] button to improve this answer to get more votes and reputation! – Brian Tompsett - 汤莱恩 Jun 16 '19 at 13:33