没有多线程使用Future有什么意义?

问题描述 投票:3回答:4

我继承了一些代码,并没有原始开发人员离开。代码使用了很多CompletableFuture,这是我第一次使用它,所以我仍然试图绕过它。据我了解,(Completable)Future通常与一些多线程机制一起使用,这将允许我们在执行耗时的任务时执行其他操作,然后通过Future获取其结果。和the javadoc一样:

interface ArchiveSearcher { String search(String target); }
class App {
    ExecutorService executor = ...
    ArchiveSearcher searcher = ...
    void showSearch(final String target) throws InterruptedException {
        Future<String> future = executor.submit(new Callable<String>() {
        public String call() {
            return searcher.search(target);
        }});
        displayOtherThings(); // do other things while searching
        try {
            displayText(future.get()); // use future
        } catch (ExecutionException ex) { cleanup(); return; }
    }
}

但是,在我继承的这个应用程序中,以下不使用任何多线程的模式出现了很多次:

public Object serve(Object input) throws ExecutionException, InterruptedException {
    CompletableFuture<Object> result = delegate1(input);
    return result.get();
}

private CompletableFuture<Object> delegate1(Object input) {
    // Do things
    return delegate2(input);
}

private CompletableFuture<Object> delegate2(Object input) {
    return CompletableFuture.completedFuture(new Object());
}

对我来说,这相当于:

public Object serve(Object input) {
    Object result = delegate1(input);
    return result;
}

private Object delegate1(Object input) {
    // Do things
    return delegate2(input);
}

private Object delegate2(Object input) {
    return new Object();
}

当然代码要复杂得多,并且在出错的情况下返回exceptionallyCompletedFuture,但是有Callable,没有Runnable,没有Executor,没有supplyAsync()没有多线程的迹象。我错过了什么?在单线程环境中使用Future有什么意义?

java multithreading asynchronous future completable-future
4个回答
2
投票

期货对于存在异步编程的情况至关重要。异步编程的一大优势是它允许您使用单个线程编写非常高效的代码。

此外,期货往往是一个全有或全无的主张。如果你想编写异步代码,你必须从上到下进行编写,即使不是每个方法都是异步的。

例如,考虑您要编写单个线程HTTP服务器,如twistedexpress。服务器的顶层(这里非常宽松的伪代码)可能看起来像:

while (true) {
  if (serverSocket.ready()) {
    connection = serverSocket.accept();
    futures.add(server.serve(connection));
  }
  for (Future future : futures) {
    if (future.isDone()) {
      Object result = future.get();
      sendResult(result);
    }
  }
  //Some kind of select-style wait here
}

只有一个线程,但任何时候发生通常需要等待的操作(从数据库,文件读取,读取请求等)它使用期货并且不会阻止一个线程,所以你有一个高性能的单一线程HTTP服务器。

现在,想象如果你的应用程序的最高级别如上所述会发生什么,并且在某些时候某些非常低级别的请求必须从文件中读取某些内容。该文件读取将产生未来。如果你们之间的所有中间层都没有处理期货,那么你将不得不阻止并且它会破坏目的。这就是为什么我说期货往往是全有或全无。

所以我的猜测是:

  1. 你的朋友目前正在做一些异步的事情,你还没有抓到它(他曾经从文件或数据库或其他任何东西中读过吗?如果是这样,他会阻止吗?)。
  2. 他有一天计划做一些异步的事情并且想要为它做好计划。
  3. 他花了很多时间在其他异步框架中,即使他没有正确使用它,也逐渐喜欢这种风格。

1
投票

是的,目前在该代码中没有使用多线程。看起来有意以这样的方式编写单线程代码,如果开发人员后来决定只使用多线程

delegate2()

方法应该修改。


0
投票

ExecutorService实现通常管理线程。我已经使用了ThreadPoolExecutor,它就是这样做的。您注释掉了代码使用的ExecutorService。


0
投票

异步代码的要点是推迟连续代码。

最常见的情况是I / O,而不是等待操作完成,你说“做你的事情并在你完成时通知我”,或者更常见的是,“做你的事情,当你做的时候这样做完成”。

这并不意味着线程。从任何设备读取,无论是网卡还是硬盘驱动器,通常都会从设备向CPU发送某种信号或中断。您可以在此期间使用CPU。 “通知我”在低级代码中更常见,您可以在其中实现调度循环或调度程序; “执行此操作”在更高级别的代码中更常见,您可以使用已为您调度和/或安排的已建立的库或框架。

不太常见的场景包括推迟执行而不阻塞线程(想想计时器与Thread.sleep())和拆分工作。实际上,拆分工作在多个线程中非常常见,您可以通过一些开销来提高性能,但对于单个线程而言则不是很多,其中开销只是开销。


您提供的代码只是构建完整的CompletableFutures,无论是成功还是异常,都是非异步代码的开销的一部分。也就是说,您仍必须遵循定义的异步样式,即使您可以立即提供结果,在这种情况下也需要为结果分配少量内存。

这可能会在每秒数千次调用,或每个线程每秒数百次调用以及数十个线程中变得明显。

有时,您可以通过预定义的完成期货进行优化,例如: null01-1,空数组/列表/流,或者您在域中可能具有的任何其他非常常见或甚至是固定的结果。类似的方法是缓存包装未来,而不仅仅是结果,而结果保持不变。但是我建议你先走这条路,然后你可能会过早地优化那些很可能不是瓶颈的东西。

© www.soinside.com 2019 - 2024. All rights reserved.