Wrapping a series of asynchronous calls with a synchronous method with a return value

余生颓废 提交于 2019-12-10 02:43:27

问题


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.



来源:https://stackoverflow.com/questions/15815907/wrapping-a-series-of-asynchronous-calls-with-a-synchronous-method-with-a-return

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