Getting a result in the future?

痴心易碎 提交于 2021-02-07 05:58:07

问题


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!


回答1:


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.




回答2:


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



回答3:


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


}


来源:https://stackoverflow.com/questions/34269015/getting-a-result-in-the-future

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!