我刚刚开始探索Java 8的一些并发功能。有一点让我感到困惑的是这两种静态方法:
CompletableFuture<Void> runAsync(Runnable runnable)
CompletableFuture<U> supplyAsync(Supplier<U> supplier)
有谁知道他们选择使用界面供应商的原因?使用Callable是不是更自然,这是Runnable的类比,它返回一个值?这是因为供应商没有抛出无法处理的异常吗?
答案 0 :(得分:11)
简短回答
不,在Callable
中使用Supplier
代替CompletableFuture.supplyAsync
并不自然。这个论点几乎完全是关于语义的,所以如果你后来仍然不相信那就没关系。
答案很长
Callable
和Supplier
功能接口/ SAM类型在功能上实际上是等效的(原谅双关语),但它们的来源和用途不同。
Callable
是作为java.util.concurrent
包的一部分创建的。这个包在Java 8中围绕lambda表达式的巨大变化之前出现,最初集中在一系列帮助你编写并发代码的工具上,而不会偏离经典的多线程经典模型。
Callable
的主要目的是抽象一个可以在不同的线程中执行并返回结果的动作。来自Callable
的Javadoc:
Callable
界面与Runnable
类似,两者都是 设计用于其实例可能由其执行的类 另一个线程。
Supplier
是作为java.util.function
包的一部分创建的。该包是Java 8中上述变化的组成部分。它提供了lambda表达式和方法引用可以作为目标的常用函数类型。
一个这样的类型是没有返回结果的参数的函数(即提供某种类型或Supplier
函数的函数)。
那么为什么Supplier
而不是Callable
?
CompletableFuture
是java.util.concurrent
包添加的一部分,它受到Java 8中上述变化的启发,允许开发人员以功能性,隐式可并行化的方式构建其代码,而不是明确地处理其中的并发。
它的supplyAsync
方法需要一种方法来提供特定类型的结果,并且它对此结果更感兴趣,而不是为达到此结果而采取的操作。它也不一定关心特殊完成(另见下面的...... 段落。)
仍然,如果Runnable
用于无参数,无结果功能接口,不应该Callable
用于无参数,单结果功能接口?强>
不一定。
java.util.function
中未包含对没有参数但未返回结果的函数的抽象(因此完全通过对外部上下文的副作用进行操作)。这意味着(有点烦人)Runnable
用于需要这种功能接口的地方。
Exception
可以抛出的已检查Callable.call()
怎么办?
这是Callable
和Supplier
之间预期语义差异的一个小标志。
Callable
是一个可以在另一个线程中执行的动作,它允许您检查其执行后的副作用。如果一切顺利,您将得到特定类型的结果,但由于执行某些操作时会出现异常情况(特别是在多线程上下文中),您可能还需要定义和处理这种异常情况。
另一方面,Supplier
是您依赖于提供某种类型对象的函数。特殊情况不应该成为Supplier
的直接消费者的责任。这是因为:
Exception
可以是一个单独的阶段,以防您关心Exception
会显着降低功能接口,lambda表达式和方法引用的表达能力