2016-03-28 28 views
14

我刚开始探索的Java 8的一些并发特性的一件事弄得我有点是这两个静态方法:CompletableFuture supplyAsync

CompletableFuture<Void> runAsync(Runnable runnable) 
CompletableFuture<U> supplyAsync(Supplier<U> supplier) 

不要任何人知道为什么他们选择使用的接口供应商?使用Callable是否更自然?这与Runnable的类比是否会返回值?那是因为供应商不会抛出无法处理的异常吗?

+0

因为他们刚刚从C#,这通常命名为'行动<>'和'不同Func <>'接口,它们完成相同的工作,但不会让程序员感到困惑。所以他们扩大了具有相同签名但不同名称的方法的功能接口。他们可能认为这会帮助程序员提供一个合作的心理模型,但它不会:这只是一个小小的PITA。 – davidbak

回答

7

简短的回答

不,这不是更自然的CompletableFuture.supplyAsync使用Callable,而不是Supplier。这个论点几乎完全是关于语义的,所以如果你之后仍然不确定,那就没关系。

龙答案

CallableSupplier功能接口/ SAM类型在功能上几乎相等(原谅双关语),但其来源和用途不同。

Callable被创建为java.util.concurrent包的一部分。这个包来自Java 8中关于lambda表达式的巨大变化,最初集中在帮助您编写并发代码的一系列工具上,而不会偏离经典的动态多线程模型。

Callable的主要用途是抽象可以在不同线程中执行并返回结果的动作。从Callable的Javadoc中:

Callable接口类似于Runnable,在两者都 设计用于一个其实例可能受 另一个线程执行。

Supplier被作为java.util.function包的一部分创建的。该包作为Java 8上述更改的组成部分。它提供了可以由lambda表达式和方法引用定位的常用函数类型。

一种这样的类型是没有参数返回结果的函数(即提供某种类型函数或函数)。

那么为什么Supplier而不是Callable

CompletableFuture是添加到由在Java中8的上述变化的启发,允许显影剂以构建在功能,隐含并行方式他的代码java.util.concurrent包的一部分,而不是在它明确地处理并发。

它的supplyAsync方法需要一种方法来提供特定类型的结果,并且对结果更感兴趣,而不是为了达到这个结果而采取的行动。它也不一定关心异常完成(另请参阅下面的...段落)。

但是,如果Runnable用于无参数,无结果功能接口,不应使用Callable用于无参数,单结果功能接口?

不一定。

对于没有参数且不返回结果(因此完全通过外部上下文中的副作用操作)的函数的抽象不包括在java.util.function中。这意味着(有些恼人地)Runnable被用在任何需要这种功能接口的地方。

那么Callable.call()可以抛出的支票Exception怎么办?

这是CallableSupplier之间预期语义差异的一个小符号。

A Callable是一个动作,可以在另一个线程中执行,并允许您检查其副作用的执行结果。如果一切顺利,您会得到特定类型的结果,但由于在执行某些操作时(特别是在多线程环境中)可能会出现异常情况,因此您可能还需要定义和处理这些异常情况。

另一方面,A Supplier是您依赖于提供某种类型的对象的函数。作为Supplier的直接消费者,例外情况不一定是您的责任。这是因为:

  1. ...功能接口通常用于在创建或变异数据,以及处理Exception可以均是一个独立的阶段,多阶段过程定义一个特定的阶段,如果你不在乎
  2. ...明确处理Exception小号显著减少的功能接口,Lambda表达式和方法表达能力引用
+0

我认为你的论点主要区别在于语义方面,而不是实践方面是正确的。在语义上使用名为_CallAsync_的方法并不意味着它会返回结果。 –

+0

IMO对于同一个功能_(例如'call()'vs'get()')具有不同方法分别命名的函数接口的想法是不妥当的,会导致很多烦恼。其中两个特别是:试图记住你需要调用的方法是什么,然后不容易发送你需要Callable等方法的供应商等等。你描述的预期“语义”都是而且与编译器强制执行的任何内容无关,在Java文档中根本没有描述或根本没有描述。 C#做得更好。 – davidbak

相关问题