0

I've written some code that essentially is responsible for orchestrating a number of API's in sequence through a library method I provide to my clients called "orchestrate" (yes I know so original). What sits behind this orchestrate method is nothing more than a loop that executes API's in the order they are received, which in turn are delegated to a number of classes that contain some business logic for building a request, calling an API, performing some validation on the response and finally returning the api result. So, if a client sent in a list of apis: {a1, a2, a3, a4, a5} it would execute each api in sequence in a completely blocking way.

I'm attempting to beef this up to where I'm able to call multiple API's in parallel depending on how I receive the instructions from a client. Think of it as the client sending me a list of lists like: { {a1, a2}, {a3}, {a4, a5} } This means I'd like to execute a1 and a2 in parallel (which means build their request, call the apis, validate the response). Then wait until i'm sure both of them are done. Then execute a3, and wait until i'm sure it's done. Finally I want to execute a4 and a5 and follow the usual pattern.

Now, I'm tempted to use futures for the simple abstraction they provide to execute methods a wait for the response using the .get() method. But what I noticed that the executorService needs underneath is future's invocation of a call() method. This is fine, but makes me think that the call() method that is implemented per class may need access to "local" data in order to do its job (after all, I can't pass the call() method any particular parameters). I really want to avoid holding any local mutable state because that brings its own side-effects.

Is there a way for me to NOT hold local state and still use futures to handle my multithreading use-case? Or is my understanding of futures completely wrong and I'm missing something obvious? If not, are there any recommendations on a good path forward with some alternatives?

BSJ
  • 1,185
  • 2
  • 10
  • 15
  • I don't follow you. You submit a Callable to the ExecutorService, and it returns you a Future. The Future's value, when it's done, is the value returned by the Callable's call() method. What mutable state are you talking about here? Any concrete code example? – JB Nizet Mar 07 '15 at 23:25
  • Yes, the `call()` method may want to use or pass some local parameters to do it's job, but if any of those parameters are defined outside of the call method itself, they'll have to be marked as `final`, and can't have their values changed, if that makes you worry any less. (Of course if they are references to mutable objects the state of those objects could still change --- and maybe that's what is worrying you?) – jas Mar 07 '15 at 23:29
  • @JBNizet I'm talking about the part where I submit the Callable to the executorService. Say I had a class ApiClass.java that worked in a blocking way and took 2 parameters like arg1 and arg2. Now I want it to be able to handle the same stuff but in a non-blocking way by returning a future. First step is I could have ApiClass implement Callable. If I want to go further and capture the appropriate callable my concern was requiring to "hold" arg1 and arg2 as a local mutable state in ApiClass.java. Alternatively, I would create a private class to hold these args, but I need to "park" them somewher – BSJ Mar 07 '15 at 23:41

1 Answers1

1

OK, so you have a class which gets you some data from a web page in a blocking way, and takes some arguments:

public class DataFetcher {
    public Data fetchData(int param1, int param2) {
        // ...
    }
}

And now you want to execute this method twice, concurrently, and get back futures. So you just need to create a Callable:

final DataFetcher fetcher = new DataFetcher();
Callable<Data> task1 = new Callable<>() {
    @Override
    public Data call() {
        return fetcher.fetchData(1, 2);
     }
};
Callable<Data> task2 = new Callable<>() {
    @Override
    public Data call() {
        return fetcher.fetchData(3, 4);
     }
};

Future<Data> result1 = executorService.submit(task1);
Future<Data> result2 = executorService.submit(task2);

I don't see any mutable state here.

To avoid repeating the code and using anonymous classes, you can define a top-level class:

public DataFetcherTask implements Callable<Data> {

    private final DataFetcher fetcher;
    private final int param1;
    private final int param2;

    public DataFetcherTask(DataFetcher fetcher, int p1, int p1) {
        this.fetcher = fetcher;
        this.param1 = p1;
        this.param2 = p2;
    }

    @Override
    public Data call() {
        return fetcher.fetchData(param1, param2);
    }
};

and then use it like this:

Future<Data> result1 = executorService.submit(new DataFetcherTask(fetcher, 1, 2));
Future<Data> result2 = executorService.submit(new DataFetcherTask(fetcher, 3, 4));

Still no trace of mutable state here.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thank you for providing an example. I may have missed something, but how are your callables "getting" the param1 and param2? I know you've hardcoded them above for the purposes of illustration, but I'm more curious about how they are "injected" into the fetchData method. I will edit my answer using your example as a baseline to clarify my question so it's more crisp. – BSJ Mar 07 '15 at 23:54
  • Thanks much for your comment. I kept getting hung up on DataFetcher itself needing to hold the parameters locally if it implemented Callable (which it would have to); it didn't occur to me that I could create another class internally like DataFetcherTask that could instead implement the Callable and hold the arguments in a completely safe way. – BSJ Mar 08 '15 at 00:00