【问题标题】:Java Future : How to unblock Main thread while executing Aysnc callJava Future:如何在执行异步调用时解除阻塞主线程
【发布时间】:2019-04-11 09:14:46
【问题描述】:

当我使用 ExecutorService 进行异步调用时,它会返回 Future Object。根据它返回的布尔值,我必须记录异步调用的状态。

但是当我尝试从future对象调用get方法时,它阻塞了主线程的执行。

是否可以解除对主线程执行的阻塞?

public class FutureExample {

    static HystrixCommand<Boolean> hystrixCommand;

    public FutureExample(HystrixCommand<Boolean> hystrixCommand){
        FutureExample.hystrixCommand = hystrixCommand;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {


        Boolean something = asyncCall();

        if(something) {
            System.out.println("Future task is done");
        }

        System.out.println("Don't wait for async call");

    }

    private static Boolean asyncCall() throws InterruptedException, ExecutionException {

        Future<Boolean> response = hystrixCommand.queue(); // Aysnc Call to remote server

        return response.get(); //this is blocking main thread
    }

}

【问题讨论】:

  • 是的,你可以,只是不要打电话给response.get();。或者您可以稍等片刻,然后致电response.isDone()
  • 但我想根据aysnc响应登录:(
  • 因此,您必须等待操作完成(例如通过调用get())才能获得结果,或者继续执行而不关心结果。您也可以启动另一个线程并在其中调用response.get();,然后在结果可用时记录结果。

标签: java concurrency java-8 future completable-future


【解决方案1】:

future 的好处是能够释放线程直到答案到来。 所以我建议你使用 Future 实现,比如 CompletableFuture:

final ExecutorService executorService = Executors.newFixedThreadPool(10);

CompletableFuture.supplyAsync(() -> {
    try {
        return hystrixCommand.queue();
    } catch (Exception e) {
        return false;
    }
}, executorService);

这将在另一个线程上工作,当未来结束时,它将完成。

【讨论】:

  • 将异步调用封装到另一个异步调用中有何意义?
  • 好吧,我已经把重点放在了一个如何从主线程中调用的例子上,这样它就会被理解为不阻塞它。
  • 根据问题,调用hystrixCommand.queue()已经是异步的,非阻塞的,返回一个Future。实际的阻塞操作是在未来调用get()。当然,您的代码没有调用get(),从而解决了问题,但是这可以更简单地实现,只需将hystrixCommand.queue(); 作为一个语句。甚至可以像executorService.submit( () -&gt; hystrixCommand.queue()); 一样简单地将不必要的包装到另一个异步操作中。这里使用的CompletableFuture 只是一个不必要的复杂化。
【解决方案2】:

根据JavaDocsget() 方法在必要时等待计算完成,然后检索其结果。

如果您想在任务完成后获得结果,请使用isDone() 函数,如果任务完成(正常、异常等),该函数返回true。然后调用get()

此外,您可以使用get(long timeout, TimeUnit unit) 函数仅等待给定的时间段。在这种情况下,如果超时或任务已完成,主线程将自动“解除阻塞”。

【讨论】:

  • 是的,我了解这些方法的作用。但我不想阻塞主线程。
  • 那就不要屏蔽了。只有当您调用 get() 方法时,它才会被阻止。您想在任务完成后得到结果,对吗?使用isDone() 检查是否是这样。它不会阻塞主线程。在执行任务时做一些其他工作(即isDone() == false)。当你准备好后,回来再次检查任务是否完成。
【解决方案3】:

如果您需要在异步任务运行时在主线程中执行代码,则需要重新设计您的 asyncCall 方法以使其返回未来。

一个例子:

private static Future<Boolean> asyncCall() 
      throws InterruptedException, ExecutionException {

    return hystrixCommand.queue(); 
}

这样,main 方法就何时阻塞/等待进行调用:

public static void main(String[] args) 
     throws InterruptedException, ExecutionException {

    Future<Boolean> something = asyncCall();

    //do something while async call is running

    //to check whether it's done running:
    if(something.isDone()) {
        System.out.println("Future task is done");
    }

    //when you're finally ready to wait:
    System.out.println("Waiting for async call to finish");
    Boolean result = something.get();
}

【讨论】:

  • 但是异步调用需要超过2秒,不想阻塞主线程直到时间。是否可以登录单独的线程或任何回调方法?
  • @NaghaveerGowda 您可以运行我用//do something while async call is running 注释标记的其他代码,并仅在您需要结果时阻止。如果您需要回调,请更新问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多