10

我当前的代码使用了一系列异步过程,这些过程最终产生了结果。我需要以这样一种方式包装每一个,即每一个都可以通过同步方法访问,并将结果作为返回值。我想使用执行器服务来做到这一点,以允许其中许多同时发生。我觉得 Future 可能与我的实现有关,但我想不出一个好的方法来实现这一点。

我现在拥有的:

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

这在内部工作得很好,但现在我需要将我的流程映射到一个同步方法,其返回值如下:

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

有谁知道如何优雅地实现这一点?

4

5 回答 5

9

如果要将异步操作(完成后执行回调)转换为同步/阻塞操作,可以使用阻塞队列。如果您愿意,可以将其包装在 Future 对象中。

  1. 定义一个只能容纳一个元素的阻塞队列:

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

  2. 启动您的异步进程(将在后台运行),并编写回调,以便在完成后将其结果添加到阻塞队列中。

  3. 在您的前台/应用程序线程中,从队列中获取它(),该队列会阻塞,直到元素变得可用:

    Result result = blockingQueue.take();

我之前使用 Future 之类的东西写过类似的东西(前台线程需要阻止来自远程机器的异步响应),您可以在此处找到示例代码。

于 2013-04-04T16:23:22.877 回答
1

我对 Guava 库做了类似的事情;这些链接可能会为您指明正确的方向:

是否可以使用 Guava 链接异步调用?

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

于 2013-04-04T16:00:14.040 回答
0

如果你想弄脏你的手,你可以这样做

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

或者您可以使用更高级别的并发工具,例如 BlockingQueue、Semaphore、CountdownLatch、Phaser 等。

请注意,这DoAJob不是线程安全的 - 如果两个线程stepOne同时调用,则会确保出现问题。

于 2013-04-04T16:30:19.483 回答
0

我建议使用invokeAll(..)。它将向执行者提交一组任务,并阻塞直到最后一个完成(成功/例外)。然后它返回已完成的 Future 对象列表,因此您可以循环它们并将结果合并到单个 ResultObject 中。

如果您希望以同步方式仅运行单个任务,可以使用以下命令:

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

- 编辑 -

现在我想我更了解您的需求。我假设您需要一种方法来提交独立的任务序列。请查看我在此答案中发布的代码。

于 2013-04-04T16:09:19.353 回答
0

Bumerang 是我的仅异步 http 请求库,它使用 Java -> https://github.com/hanilozmen/Bumerang为 Android http 请求构建。我需要在不接触我的图书馆的情况下进行同步调用。这是我的完整代码。npgall 的回答启发了我,谢谢!类似的方法将应用于各种异步库。

public class TestActivity extends Activity {

MyAPI api = (MyAPI) Bumerang.get().initAPI(MyAPI.class);
BlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<Object>(1);

static int indexForTesting;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            for(int i = 0; i < 10; i++) {
                getItems();
                try {
                    Object response = blockingQueue.take(); // waits for the response
                    Log.i("TAG", "index " + indexForTesting + " finished. Response " + response.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    });
    t.start();
}

void getItems() {
    Log.i("TAG", "index " + ++indexForTesting + " started");
    api.getItems(new ResponseListener<Response<List<ResponseModel>>>() {
        @Override
        public void onSuccess(Response<List<ResponseModel>> response) {
            List<ResponseModel> respModel = response.getResponse();
            try {
                blockingQueue.put(response);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onError(Response<List<ResponseModel>> response) {
            Log.i("onError", response.toString());
            try {
                blockingQueue.put(response);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
}

}

于 2020-06-07T19:32:56.967 回答