Wrapping a series of asynchronous calls with a syn

2019-04-22 04:02发布

问题:

My current code uses series of asynchronous processes that culminate in results. I need to wrap each of these in such a way that each is accessed by a synchronous method with the result as a return value. I want to use executor services to do this, so as to allow many of these to happen at the same time. I have the feeling that Future might be pertinent to my implementation, but I can't figure out a good way to make this happen.

What I have now:

public class DoAJob {
  ResultObject result;

  public void stepOne() {
    // Passes self in for a callback
    otherComponent.doStepOne(this);
  }

  // Called back by otherComponent once it has completed doStepOne
  public void stepTwo(IntermediateData d) {
    otherComponent.doStepTwo(this, d);
  }

  // Called back by otherComponent once it has completed doStepTwo
  public void stepThree(ResultObject resultFromOtherComponent) {
    result = resultFromOtherComponent;
  //Done with process
  }
}

This has worked pretty well internally, but now I need to map my process into a synchronous method with a return value like:

public ResultObject getResult(){
  // ??? What goes here ???
}

Does anyone have a good idea about how to implement this elegantly?

回答1:

If you want to turn an asynchronous operation (which executes a callback when finished), into a synchronous/blocking one, you can use a blocking queue. You can wrap this up in a Future object if you wish.

  1. Define a blocking queue which can hold just one element:

    BlockingQueue<Result> blockingQueue = new ArrayBlockingQueue<Result>(1);

  2. Start your asynchronous process (will run in the background), and write the callback such that when it's done, it adds its result to the blocking queue.

  3. In your foreground/application thread, have it take() from the queue, which blocks until an element becomes available:

    Result result = blockingQueue.take();

I wrote something similar before (foreground thread needs to block for an asynchronous response from a remote machine) using something like a Future, you can find example code here.



回答2:

I've done something similar with the Guava library; these links might point you in the right direction:

Is it possible to chain async calls using Guava?

https://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained



回答3:

If you like to get your hands dirty, you can do this

ResultObject result;

public void stepOne() 

    otherComponent.doStepOne(this);

    synchronized(this)
        while(result==null) this.wait();
    return result;

public void stepThree(ResultObject resultFromOtherComponent) 

    result = resultFromOtherComponent;

    synchronized(this)
        this.notify();

Or you can use higher level concurrency tools, like BlockingQueue, Semaphore, CountdownLatch, Phaser, etc etc.

Note that DoAJob is not thread safe - trouble ensured if two threads call stepOne at the same time.



回答4:

I recommend using invokeAll(..). It will submit a set of tasks to the executor, and block until the last one completes (successfully/with exception). It then returns a list of completed Future objects, so you can loop on them and merge the results into a single ResultObject.

In you wish to run only a single task in a synchronous manner, you can use the following:

executor.invokeAll(Collections.singleton(task)); 

--edit--

Now I think I understand better your needs. I assume that you need a way to submit independent sequences of tasks. Please take a look at the code I posted in this answer.