This discussion is archived
6 Replies Latest reply: May 4, 2012 7:14 AM by 28644 RSS

How to create Futures and in a non-blocking way process results

28644 Newbie
Currently Being Moderated
I would like to be able to create a Future task that calls a web service and when it returns, process the result, be it an Exception, SoapFault, or an actual value. But I would like to "send and forget"; in other words, I don't want the web app Controller from which I create the Future task to block waiting for the response.

I am using a ScheduledExecutorService and have tried with and without a CompletionService. I've tried the following:

1.

ScheduledExecutorService scheduledExecutorService = Executors.newFixedThreadPool(4);
ScheduledFuture<String> scheduledFuture =
scheduledExecutorService.schedule(webServiceCallTask, 5, TimeUnit.SECONDS);
scheduledFuture.get()

2.

ScheduledExecutorService scheduledExecutorService = Executors.newFixedThreadPool(4);
CompletionService<String> pool = new ExecutorCompletionService<String>(threadPool);
pool.submit(webServiceCallTask);
result = pool.take().get();

But both of these block -- the Controller waits for the response.

I think what I need is to do some kind of polling with the CompletionService ( pool.poll() ) but ... in another thread / process perhaps, not in the same method I use to schedule / submit the task.

Is this the correct / best way to do this?
  • 1. Re: How to create Futures and in a non-blocking way process results
    jtahlborn Expert
    Currently Being Moderated
    if you don't want to block, stop calling Future.get().
  • 2. Re: How to create Futures and in a non-blocking way process results
    28644 Newbie
    Currently Being Moderated
    Yes but at some point I need to get the result of the Future. So I have to call Future.get() somewhere.

    I came up with a solution but I'm not sure how "best practice" it is.
    Essentially I have a Singleton class that contains my ScheduledExecutorService and CompletionService. The Singleton class is instantiated and the ExecutorService and CompletionService are started when the app starts.

    Within that Singleton I have a schedule() method which simply does:

    public void schedule(final StringTask task) {
    completionService.submit(task);
    }

    And I also have a Thread, started in the constructor of the Singleton that does the following:

    Thread t = new Thread() {

    @Override
    public void run() {

    while (true) {
    try {
    String result = completionService.take().get();
    }
    ...
    }
    }

    So basically this Thread just sits around waiting for a Future to appear and when it does, it take()s it and calls get() to grab its result.

    So from my Controllers I call schedule(), and forget about it, returning control to the web app immediately. When my Future is done, I process the result, asyncronously.

    Any comments / suggestions? Am I an evil genius or a complete moron?
  • 3. Re: How to create Futures and in a non-blocking way process results
    796440 Guru
    Currently Being Moderated
    sygma6 wrote:
    Yes but at some point I need to get the result of the Future. So I have to call Future.get() somewhere.
    Either you know that the operation has completed, or you don't. If you know that it has completed, then you can just call get(), and it won't have any reason to block. If you don't know that it has completed, how will you decide when to get the result? Or, alternatively, what are you planning to do if, when you go to get the result, it's not ready yet?
  • 4. Re: How to create Futures and in a non-blocking way process results
    28644 Newbie
    Currently Being Moderated
    Well, the idea is that I can have anywhere from 0->n Futures waiting to be processed at any time. So I call take() when a Future appears, and get() when it's done.

    As it says in the JavaDoc:

    Future<String> java.util.concurrent.CompletionService.take()
    Retrieves and removes the Future representing the next completed task, waiting if none are yet present.

    Again, I'm not sure if this is the best way to do this, that's why I'm putting the question out there. These Tasks are going to be calls on a network. They could take anywhere from 1-10 seconds. And as I said there will likely be a bunch waiting at the same time. I can't know when each one is going to be ready for processing. I don't care what order they are processed in. I just want them processed as they become available.

    My thinking in doing it this way was:

    1. to separate Task scheduling from Task processing, and

    2. to put the burden of knowing when the Future has finished and has a result on the CompletionService.

    I've seen many examples out there like this:
    CompletionService<String> pool;

    for (int i = 0; i < 1; i++) {
    pool.submit(task);
    }

    for (int i = 0; i < 1; i++) {
    pool.take().get();
    }

    But this mixes Task scheduling with Task processing. I will never know how many tasks there are at any given time, so I can't just do a simple loop. I need a Thread out there constantly polling to see if any Futures are ready and if so, process their result.
  • 5. Re: How to create Futures and in a non-blocking way process results
    796440 Guru
    Currently Being Moderated
    sygma6 wrote:
    Well, the idea is that I can have anywhere from 0->n Futures waiting to be processed at any time. So I call take() when a Future appears, and get() when it's done.
    But, again, how are you planning to know when it's done?
    I can't know when each one is going to be ready for processing. I don't care what order they are processed in. I just want them processed as they become available.
    So then you need to call take() or get() or whatever blocking call is appropriate. And if you don't want to block because there's other work you could be doing if nothing is available, then you should put that take() or get() call into a separate thread.
  • 6. Re: How to create Futures and in a non-blocking way process results
    28644 Newbie
    Currently Being Moderated
    Well with the current setup it seems to be working just fine. In other words, I set up the ScheduledExecutorService and CompletionService when the app starts using a ServletContextListener. I also start a Thread that runs:

    String result = completionService.take().get();

    in an infinite loop. When there is nothing to take() it just waits. But if I submit a task it immediately calls get() as soon as that task is done.

    I've also submitted tasks that take over a minute to finish (I just call Thread.sleep), and while they are sleeping I submit more tasks that start and finish immediately. So their take() calls are not blocked by the get() of the task that takes a minute to finish. And when the minute is up, its get() returns its result.

    So it seems to be doing what I wanted - task submitting by the web app, task processing in a separate Thread. Based on what I am seeing I don't think I need to put the take() and the get() in different Threads, do I?

    BUT I still have a problem I hadn't realized. Currently I am doing a completionService.submit(task) in my schedule() method. Which allows me to call completionService.take().get() in my separate Thread. But that was just for testing! What I really need to do in the schedule() method is scheduledExecutorService.schedule(task, 0, TimeUnit.SECONDS). Because some tasks are executed immediately, other run every hour, etc.

    So how do I get the tasks from the scheduledExecutorService as they become available in my separate thread? The call to completionService.take().get() is no longer firing, because I am no longer submitting tasks to completionService. So it seems I need to use the completionService in order to process the tasks as they become available. But how can I schedule tasks using the completionService?

    ... it's with good reason that they say concurrency is a tough topic, in Java and in general ...

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points