Java 可完成的未来供应同步

Java 可完成的未来供应同步,java,concurrency,java-8,Java,Concurrency,Java 8,我刚刚开始探索Java8的一些并发特性。让我有点困惑的是这两种静态方法: CompletableFuture<Void> runAsync(Runnable runnable) CompletableFuture<U> supplyAsync(Supplier<U> supplier) CompletableFuture runAsync(Runnable-Runnable) 可完成的未来供应同步(供应商) 有人知道他们为什么选择使用接口供应商吗?使用C

我刚刚开始探索Java8的一些并发特性。让我有点困惑的是这两种静态方法:

CompletableFuture<Void> runAsync(Runnable runnable) 
CompletableFuture<U> supplyAsync(Supplier<U> supplier)
CompletableFuture runAsync(Runnable-Runnable)
可完成的未来供应同步(供应商)

有人知道他们为什么选择使用接口供应商吗?使用Callable不是更自然吗?Callable类似于Runnable,返回一个值?这是因为供应商没有抛出无法处理的异常吗?

简短回答

不,在
CompletableFuture.supplyAsync
中使用
Callable
而不是
Supplier
不是更自然的做法。争论几乎完全是关于语义学的,因此,如果你事后仍然感到不服气,那也没关系

长答案

可调用的
供应商
功能接口/SAM类型实际上在功能上是等效的(请原谅双关语),但它们的来源和预期用途不同

Callable
是作为
java.util.concurrent
包的一部分创建的。该软件包出现在Java8中围绕lambda表达式所做的大量更改之前,最初集中于帮助您编写并发代码的一系列工具,而没有偏离传统的动手多线程模型

Callable
的主要目的是抽象一个可以在不同线程中执行并返回结果的操作。从
Callable
的Javadoc:

Callable
接口类似于
Runnable
,两者都是 为其实例可能由执行的类而设计 另一条线

供应商
是作为
java.util.function
包的一部分创建的。该包是Java8中上述更改的一个组成部分。它提供了lambda表达式和方法引用可以针对的常见函数类型

其中一种类型是不带返回结果的参数的函数(即提供某种类型的函数或
供应商
函数)

那么为什么
供应商
不可调用?

CompletableFuture
java.util.concurrent
软件包的一部分,该软件包的灵感来自于java 8中的上述更改,它允许开发人员以功能性、隐式并行化的方式构造代码,而不是显式地处理其中的并发性

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

但是,如果
Runnable
用于无参数、无结果功能接口,那么
Callable
不应该用于无参数、单结果功能接口吗?

不一定

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

那么
Callable.call()

这是
Callable
Supplier
之间预期语义差异的一个小迹象

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

另一方面,
供应商
是您提供某种类型对象所依赖的功能。作为
供应商的直接消费者
,不一定要对特殊情况负责。这是因为:

  • …功能接口通常用于定义多阶段过程中的特定阶段,以创建或更改数据,如果您需要,处理
    异常可以是单独的阶段
  • …显式处理
    异常
    s显著降低了函数接口、lambda表达式和方法引用的表达能力

  • 我认为你认为主要区别在于语义方面而不是实际方面的论点是正确的。从语义上讲,使用名为CallAsync的方法并不意味着它会返回任何结果。在我看来,为同一功能(例如,
    call()
    vs
    get()
    )使用不同方法分别命名的函数接口的想法是错误的,并导致了很多麻烦。其中两个是:试图记住你需要调用的该死的方法是什么,然后不容易向供应商发送你需要调用的方法,等等。预期的“语义”您描述的都是文字,与编译器将强制执行的任何内容无关,并且在Java文档中描述错误或根本没有描述。C#做得更好。@davidbak当您想将
    供应商
    发送给一个需要
    可调用
    的方法时,方法名是否匹配无关紧要,因为Java中没有duck类型。另一方面,您可以简单地使用
    existingSupplier::get
    ,其中需要一个
    可调用的
    ,并且它可以独立于实际的方法名工作。因为它们必须与C不同,C