java8新特性CompletableFuture异步编程使用 和 并行流parallel,CompletableFuture与Executors线程池的使用与区别

 

带有示例的Java CompletableFuture教程| CalliCoder
https://www.callicoder.com/java-8-completablefuture-tutorial/

并行流parallel,CompletableFuture与Executors线程池的使用与区别 - R_P_J的博客 - CSDN博客
https://blog.csdn.net/r_p_j/article/details/79582946

 

 

 

 

 

带有示例的Java CompletableFuture教程

 

ajeev Kumar Singh  •  Java  •2017年7月18日•15分钟阅读

带有示例的Java CompletableFuture教程

 

Java 8提出了大量的新功能和增强功能,如  Lambda表达式,  Streams,  CompletableFutures  等。在这篇文章中,我将使用简单的示例为您提供CompletableFuture及其所有方法的详细说明。

什么是CompletableFuture?

CompletableFuture用于Java中的异步编程。异步编程是一种通过在主应用程序线程之外的单独线程上运行任务来  编写非阻塞代码的方法,并通知主线程有关其进度,完成或失败的信息。

这样,您的主线程不会阻塞/等待任务完成,它可以并行执行其他任务。

具有这种并行性可以极大地提高程序的性能。

另请阅读: Java并发和多线程基础知识

Future vs. CompletableFuture

CompletableFuture是Java 5中引入的Java Future API的扩展。

Future用作异步计算结果的参考。它提供了  isDone()一种检查计算是否完成的get() 方法,以及一种在完成计算时检索计算结果的方法。

您可以从我的Callable和Future Tutorial中了解有关Future的更多信息。

未来的API是迈向Java异步编程的一个很好的步骤,但它缺少一些重要且有用的功能 -

未来的局限

  1. 它不能手动完成:

    假设您已经编写了一个函数来从远程API获取电子商务产品的最新价格。由于此API调用非常耗时,因此您将在单独的线程中运行它并从函数中返回的未来。

    现在,假设如果远程API服务已关闭,那么您希望通过产品的最后一个缓存价格手动完成的未来。

    你能用未来做到这一点吗?没有!

  2. 您无法阻止对未来的结果执行进一步操作:

    未来未通知您其完成情况。它提供了  get() 一种阻塞方法,直到结果可用。

    您无法将回调函数附加到未来,并在未来的结果可用时自动调用它。

  3. 多个期货不能链接在一起:

    有时您需要执行长时间运行的计算,并且在计算完成后,您需要将其结果发送到另一个长时间运行的计算,依此类推。

    您无法使用期货创建此类异步工作流。

  4. 您不能将多个期货组合在一起:

    假设您有10个不同的期货要并行运行,然后在所有期货完成后运行一些功能。你不能用未来做到这一点。

  5. 没有异常处理:

    Future API没有任何异常处理构造。

哇!这么多限制对吧?好吧,这就是为什么我们有CompletableFuture。您可以使用CompletableFuture实现上述所有功能。

CompletableFuture实现  Future 和  CompletionStage 接口,并提供了大量便利方法,用于创建,链接和组合多个期货。它还具有非常全面的异常处理支持。

创建CompletableFuture

1.琐碎的例子 -

您只需使用以下no-arg构造函数即可创建CompletableFuture -

CompletableFuture<String> completableFuture = new CompletableFuture<String>();

这是您可以拥有的最简单的CompletableFuture。想要获得此CompletableFuture结果的所有客户都可以调用  CompletableFuture.get() 方法 -

String result = completableFuture.get()

该  get() 方法将阻塞,直到Future完成。因此,上述调用将永远阻止,因为Future永远不会完成。

您可以使用  CompletableFuture.complete() 方法手动完成未来 -

completableFuture.complete("Future's Result")

等待此Future的所有客户都将获得指定的结果。并且,后续调用  completableFuture.complete() 将被忽略。

2.使用runAsync() - 运行异步计算 

如果要异步运行某些后台任务而不想从任务中返回任何内容,则可以使用  CompletableFuture.runAsync() 方法。它需要一个  Runnable  对象并返回  CompletableFuture<Void>

// Run a task specified by a Runnable Object asynchronously.
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
    @Override
    public void run() {
        // Simulate a long-running Job
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
        System.out.println("I'll run in a separate thread than the main thread.");
    }
});

// Block and wait for the future to complete
future.get()

You can also pass the Runnable object in the form of a lambda expression -

// Using Lambda Expression
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    // Simulate a long-running Job   
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    System.out.println("I'll run in a separate thread than the main thread.");
});

In this post, I’ll use lambda expressions very frequently, and you should use it too if you’re not already using it in your Java code.

3. Run a task asynchronously and return the result using supplyAsync() -

CompletableFuture.runAsync() is useful for tasks that don’t return anything. But what if you want to return some result from your background task?

Well, CompletableFuture.supplyAsync() is your companion. It takes a Supplier<T> and returns CompletableFuture<T> where T is the type of the value obtained by calling the given supplier -

// Run a task specified by a Supplier object asynchronously
CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
    @Override
    public String get() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
        return "Result of the asynchronous computation";
    }
});

// Block and get the result of the Future
String result = future.get();
System.out.println(result);

Supplier<T> is a simple functional interface which represents a supplier of results. It has a single get() method where you can write your background task and return the result.

Once again, you can use Java 8’s lambda expression to make the above code more concise -

// Using Lambda Expression
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
});

A note about Executor and Thread Pool -

You might be wondering that - Well, I know that the runAsync() and supplyAsync() methods execute their tasks in a separate thread. But, we never created a thread right?

Yes! CompletableFuture executes these tasks in a thread obtained from the global ForkJoinPool.commonPool().

But hey, you can also create a Thread Pool and pass it to runAsync() and supplyAsync() methods to let them execute their tasks in a thread obtained from your thread pool.

All the methods in the CompletableFuture API has two variants - One which accepts an Executor as an argument and one which doesn’t -

// Variations of runAsync() and supplyAsync() methods
static CompletableFuture<Void>  runAsync(Runnable runnable)
static CompletableFuture<Void>  runAsync(Runnable runnable, Executor executor)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

Here’s how you can create a thread pool and pass it to one of these methods -

Executor executor = Executors.newFixedThreadPool(10);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }
    return "Result of the asynchronous computation";
}, executor);

Transforming and acting on a CompletableFuture

The CompletableFuture.get() method is blocking. It waits until the Future is completed and returns the result after its completion.

But, that’s not what we want right? For building asynchronous systems we should be able to attach a callback to the CompletableFuture which should automatically get called when the Future completes.

That way, we won’t need to wait for the result, and we can write the logic that needs to be executed after the completion of the Future inside our callback function.

You can attach a callback to the CompletableFuture using thenApply()thenAccept() and thenRun() methods -

1. thenApply()

You can use thenApply() method to process and transform the result of a CompletableFuture when it arrives. It takes a Function<T,R> as an argument. Function<T,R> is a simple functional interface representing a function that accepts an argument of type T and produces a result of type R -

// Create a CompletableFuture
CompletableFuture<String> whatsYourNameFuture = CompletableFuture.supplyAsync(() -> {
   try {
       TimeUnit.SECONDS.sleep(1);
   } catch (InterruptedException e) {
       throw new IllegalStateException(e);
   }
   return "Rajeev";
});

// Attach a callback to the Future using thenApply()
CompletableFuture<String> greetingFuture = whatsYourNameFuture.thenApply(name -> {
   return "Hello " + name;
});

// Block and get the result of the future.
System.out.println(greetingFuture.get()); // Hello Rajeev

You can also write a sequence of transformations on the CompletableFuture by attaching a series of thenApply() callback methods. The result of one thenApply() method is passed to the next in the series -

CompletableFuture<String> welcomeText = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Rajeev";
}).thenApply(name -> {
    return "Hello " + name;
}).thenApply(greeting -> {
    return greeting + ", Welcome to the CalliCoder Blog";
});

System.out.println(welcomeText.get());
// Prints - Hello Rajeev, Welcome to the CalliCoder Blog

2. thenAccept() and thenRun()

If you don’t want to return anything from your callback function and just want to run some piece of code after the completion of the Future, then you can use thenAccept() and thenRun() methods. These methods are consumers and are often used as the last callback in the callback chain.

CompletableFuture.thenAccept() takes a Consumer<T> and returns CompletableFuture<Void>. It has access to the result of the CompletableFuture on which it is attached.

// thenAccept() example
CompletableFuture.supplyAsync(() -> {
	return ProductService.getProductDetail(productId);
}).thenAccept(product -> {
	System.out.println("Got product detail from remote service " + product.getName())
});

While thenAccept() has access to the result of the CompletableFuture on which it is attached, thenRun() doesn’t even have access to the Future’s result. It takes a Runnable and returns CompletableFuture<Void> -

// thenRun() example
CompletableFuture.supplyAsync(() -> {
    // Run some computation  
}).thenRun(() -> {
    // Computation Finished.
});

A note about async callback methods -

All the callback methods provided by CompletableFuture have two async variants -

// thenApply() variants
<U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

These async callback variations help you further parallelize your computations by executing the callback tasks in a separate thread.

Consider the following example -

CompletableFuture.supplyAsync(() -> {
    try {
       TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }
    return "Some Result"
}).thenApply(result -> {
    /* 
      Executed in the same thread where the supplyAsync() task is executed
      or in the main thread If the supplyAsync() task completes immediately (Remove sleep() call to verify)
    */
    return "Processed Result"
})

In the above case, the task inside thenApply() is executed in the same thread where the supplyAsync() task is executed, or in the main thread if the supplyAsync() task completes immediately (try removing sleep() call to verify).

To have more control over the thread that executes the callback task, you can use async callbacks. If you use thenApplyAsync() callback, then it will be executed in a different thread obtained from ForkJoinPool.commonPool() -

CompletableFuture.supplyAsync(() -> {
    return "Some Result"
}).thenApplyAsync(result -> {
    // Executed in a different thread from ForkJoinPool.commonPool()
    return "Processed Result"
})

Moreover, If you pass an Executor to the thenApplyAsync() callback then the task will be executed in a thread obtained from the Executor’s thread pool.

Executor executor = Executors.newFixedThreadPool(2);
CompletableFuture.supplyAsync(() -> {
    return "Some result"
}).thenApplyAsync(result -> {
    // Executed in a thread obtained from the executor
    return "Processed Result"
}, executor);

Combining two CompletableFutures together

1. Combine two dependent futures using thenCompose() -

Let’s say that you want to fetch the details of a user from a remote API service and once the user’s detail is available, you want to fetch his Credit rating from another service.

Consider the following implementations of getUserDetail() and getCreditRating() methods -

CompletableFuture<User> getUsersDetail(String userId) {
	return CompletableFuture.supplyAsync(() -> {
		UserService.getUserDetails(userId);
	});	
}

CompletableFuture<Double> getCreditRating(User user) {
	return CompletableFuture.supplyAsync(() -> {
		CreditRatingService.getCreditRating(user);
	});
}

Now, Let’s understand what will happen if we use thenApply() to achieve the desired result -

CompletableFuture<CompletableFuture<Double>> result = getUserDetail(userId)
.thenApply(user -> getCreditRating(user));

In earlier examples, the Supplier function passed to thenApply() callback would return a simple value but in this case, it is returning a CompletableFuture. Therefore, the final result in the above case is a nested CompletableFuture.

If you want the final result to be a top-level Future, use thenCompose() method instead -

CompletableFuture<Double> result = getUserDetail(userId)
.thenCompose(user -> getCreditRating(user));

所以,这里的经验法则 - 如果你的回调函数返回一个CompletableFuture,并且你想要一个来自CompletableFuture链的扁平化结果(在大多数情况下你会这样做),那么就使用  thenCompose()

2.使用thenCombine()组合两个独立的期货 -

虽然  thenCompose() 用于组合两个期货,一个其中期货依赖于另一个  thenCombine() 期货,但是当您希望两个期货独立运行并在两个期货完成后执行某些操作时使用。

System.out.println("Retrieving weight.");
CompletableFuture<Double> weightInKgFuture = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return 65.0;
});

System.out.println("Retrieving height.");
CompletableFuture<Double> heightInCmFuture = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return 177.8;
});

System.out.println("Calculating BMI.");
CompletableFuture<Double> combinedFuture = weightInKgFuture
        .thenCombine(heightInCmFuture, (weightInKg, heightInCm) -> {
    Double heightInMeter = heightInCm/100;
    return weightInKg/(heightInMeter*heightInMeter);
});

System.out.println("Your BMI is - " + combinedFuture.get());

到传递的回调函数  thenCombine() 将在两个期货完成时被调用。

将多个CompletableFutures组合在一起

我们使用  thenCompose() 和  thenCombine() 两个CompletableFutures结合在了一起。现在,如果你想组合任意数量的CompletableFutures怎么办?那么,您可以使用以下方法组合任意数量的CompletableFutures -

static CompletableFuture<Void>	 allOf(CompletableFuture<?>... cfs)
static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

1. CompletableFuture.allOf()

CompletableFuture.allOf 当您拥有要并行运行的独立期货列表并在所有期货完成后执行某些操作时,会使用此方案。

假设您要下载网站的100个不同网页的内容。您可以按顺序执行此操作,但这将花费大量时间。所以,你编写了一个带有网页链接的函数,并返回一个CompletableFuture,即它以异步方式下载网页的内容 -

CompletableFuture<String> downloadWebPage(String pageLink) {
	return CompletableFuture.supplyAsync(() -> {
		// Code to download and return the web page's content
	});
} 

现在,当下载所有网页时,您需要计算包含关键字的网页数量 - 'CompletableFuture'。让我们  CompletableFuture.allOf() 来实现这个目标 -

List<String> webPageLinks = Arrays.asList(...)	// A list of 100 web page links

// Download contents of all the web pages asynchronously
List<CompletableFuture<String>> pageContentFutures = webPageLinks.stream()
        .map(webPageLink -> downloadWebPage(webPageLink))
        .collect(Collectors.toList());


// Create a combined Future using allOf()
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
        pageContentFutures.toArray(new CompletableFuture[pageContentFutures.size()])
);

问题  CompletableFuture.allOf() 是它返回  CompletableFuture<Void>。但是我们可以通过编写额外的几行代码来获取所有包装的CompletableFutures的结果 -

// When all the Futures are completed, call `future.join()` to get their results and collect the results in a list -
CompletableFuture<List<String>> allPageContentsFuture = allFutures.thenApply(v -> {
   return pageContentFutures.stream()
           .map(pageContentFuture -> pageContentFuture.join())
           .collect(Collectors.toList());
});

花点时间了解上面的代码片段。由于我们future.join() 在所有期货都已完成时打电话,我们并没有阻止任何地方:-)

该  join() 方法类似于  get()。唯一的区别是,如果底层的CompletableFuture异常完成,它会抛出一个未经检查的异常。

现在让我们计算包含我们关键字的网页数量 -

// Count the number of web pages having the "CompletableFuture" keyword.
CompletableFuture<Long> countFuture = allPageContentsFuture.thenApply(pageContents -> {
    return pageContents.stream()
            .filter(pageContent -> pageContent.contains("CompletableFuture"))
            .count();
});

System.out.println("Number of Web Pages having CompletableFuture keyword - " + 
        countFuture.get());

2. CompletableFuture.anyOf()

CompletableFuture.anyOf() 顾名思义,返回一个新的CompletableFuture,它在任何给定的CompletableFutures完成时完成,结果相同。

考虑以下示例 -

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Result of Future 1";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Result of Future 2";
});

CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
       throw new IllegalStateException(e);
    }
    return "Result of Future 3";
});

CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);

System.out.println(anyOfFuture.get()); // Result of Future 2

在上面的示例中,  anyOfFuture 当三个CompletableFutures中的任何一个完成时完成。由于  future2 睡眠时间最少,它将首先完成,最终结果将是 - 未来的结果2。

CompletableFuture.anyOf() takes a varargs of Futures and returns CompletableFuture<Object>. The problem with CompletableFuture.anyOf() is that if you have CompletableFutures that return results of different types, then you won’t know the type of your final CompletableFuture.

CompletableFuture Exception Handling

We explored How to create CompletableFuture, transform them, and combine multiple CompletableFutures. Now let’s understand what to do when anything goes wrong.

Let’s first understand how errors are propagated in a callback chain. Consider the following CompletableFuture callback chain -

CompletableFuture.supplyAsync(() -> {
	// Code which might throw an exception
	return "Some result";
}).thenApply(result -> {
	return "processed result";
}).thenApply(result -> {
	return "result after further processing";
}).thenAccept(result -> {
	// do something with the final result
});

If an error occurs in the original supplyAsync() task, then none of the thenApply() callbacks will be called and future will be resolved with the exception occurred. If an error occurs in first thenApply()callback then 2nd and 3rd callbacks won’t be called and the future will be resolved with the exception occurred, and so on.

1. Handle exceptions using exceptionally() callback

The exceptionally() callback gives you a chance to recover from errors generated from the original Future. You can log the exception here and return a default value.

Integer age = -1;

CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
    if(age < 0) {
        throw new IllegalArgumentException("Age can not be negative");
    }
    if(age > 18) {
        return "Adult";
    } else {
        return "Child";
    }
}).exceptionally(ex -> {
    System.out.println("Oops! We have an exception - " + ex.getMessage());
    return "Unknown!";
});

System.out.println("Maturity : " + maturityFuture.get()); 

Note that, the error will not be propagated further in the callback chain if you handle it once.

2. Handle exceptions using the generic handle() method

API还提供了一种更通用的方法 -  handle() 从异常中恢复。无论是否发生异常,都会调用它。

Integer age = -1;

CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
    if(age < 0) {
        throw new IllegalArgumentException("Age can not be negative");
    }
    if(age > 18) {
        return "Adult";
    } else {
        return "Child";
    }
}).handle((res, ex) -> {
    if(ex != null) {
        System.out.println("Oops! We have an exception - " + ex.getMessage());
        return "Unknown!";
    }
    return res;
});

System.out.println("Maturity : " + maturityFuture.get());

如果发生异常,则  res 参数将为null,否则  ex 参数将为null。

结论

恭喜大家!  在本教程中,我们探讨了CompletableFuture API最有用和最重要的概念。

谢谢你的阅读。我希望这篇博文对你有所帮助。请在下面的评论部分告诉我您的观点,问题和评论。

 

 

 

 

 

 

 

 

 

 

 

===========================================================================

===========================================================================

===========================================================================

 

 

并行流parallel,CompletableFuture与Executors线程池的使用与区别 - R_P_J的博客 - CSDN博客
https://blog.csdn.net/r_p_j/article/details/79582946

 

 

 

并行流parallel,CompletableFuture与Executors线程池的使用与区别

2018年03月16日 15:57:53 R_P_J 阅读数:1612

list并行操作在项目开发可以极大提高代码效率与性能,java8对其进行了很好的封装,简单使用研究一下:

1. 先自己创建一个list:

// list在实际使用中要注意线程安全,Collections.synchronizedList写操作性能高,CopyOnWriteArrayList读操作性能较好
List<String> list = Arrays.asList(new String[10000]);
  •  

2. parallel并行流使用:

list.stream().parallel().forEach(a -> {
            // 操作代码.....
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
  •  

并行流特点:

基于服务器内核的限制,如果你是八核,每次线程只能起八个,不能自定义线程池;
适用于对list密集计算操作充分利用CPU资源,如果需要调用远端服务不建议使用;
  •  

3. CompletableFuture使用

3.1 未使用自定义线程池:

// supplyAsync需要有返回值,runAsync不需要有返回值
list.stream().map(a -> CompletableFuture.supplyAsync(() -> {
             // 操作代码.....
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return a;
        })).collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList());
  •  

划重点:

未自定义线程池时默认线程池跟并行流一样,都是根据服务器内核数创建线程数量。
  • 1

3.2 使用自定义线程池:


ExecutorService executor = Executors.newFixedThreadPool(Math.min(list.size(), 100));

list.stream().map(a -> CompletableFuture.supplyAsync(() -> {
            // 操作代码.....
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return a;
        }, executor)).collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList());
  •  

补充:

1. 线程数量的计算公式:

T(线程数) = N(服务器内核数) * u(期望cpu利用率) * (1 + E(等待时间)/C(计算时间));
  • 1

2. 获取服务器内核数:

int count = Runtime.getRuntime().availableProcessors();
  • 1

3.划重点:

此处join方法和CompletableFuture的get()方法类似,都是阻塞线程,等待结果,但是join方法不抛异常,不需要处理异常,让你代码更方便,get方法抛异常。

4. Executors使用(有多种线程池)

list.forEach(a ->
            executor.submit(() -> {
                // 操作代码.....
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            })
        );
        executor.shutdown();
        while (true) {
            if (executor.isTerminated()) {
                System.out.println("线程执行完毕!!!");
                break;
            }
            Thread.sleep(10);
        }
  •  

5. 简单总结:

可以将代码粘贴到idea中运行把运行时间打出来看看效果,最后发现:
  • 1
  • parallel与未定义自线程池的CompletableFuture效果差别不大,原因是底层都使用的默认的线程池;
  • CompletableFuture自定义线程池与Executors的运行效果差别不大,但CompletableFuture有很多组合式的异步编程方法: 
    runAsync:异步执行没有返回值; 
    supplyAsync:异步执行有返回值; 
    thenApply:继续执行当前线程future完成的函数,不需要阻塞等待其处理完成; 
    thenApplyAsync:在不同线程池异步地应用参数中的函数; 
    thenCompose:用于多个彼此依赖的futrue进行串联起来 
    thenCombine:并联起两个独立的future,注意,这些future都是在长时间计算都完成以后 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值