Concurrency Java 8,然后是apply()和accept()

Concurrency Java 8,然后是apply()和accept(),concurrency,java-8,Concurrency,Java 8,我正在为链接completablefuture操作创建一个演示示例,我想我已经接近了,但我缺少了一些东西。除了main()中的最后一个“Build cakes in parallel”子句外,所有内容都可以编译: import java.util.*; 导入java.util.concurrent.*; 导入java.util.function.*; 导入java.util.stream.*; 导入java.time.*; //使用装饰图案构建蛋糕: 接口蛋糕{ 字符串descripe(); }

我正在为链接
completablefuture
操作创建一个演示示例,我想我已经接近了,但我缺少了一些东西。除了
main()
中的最后一个“Build cakes in parallel”子句外,所有内容都可以编译:

import java.util.*;
导入java.util.concurrent.*;
导入java.util.function.*;
导入java.util.stream.*;
导入java.time.*;
//使用装饰图案构建蛋糕:
接口蛋糕{
字符串descripe();
}
类蛋糕工具蛋糕{
私有int-id;
公共蛋糕(int-id){this.id=id;}
@凌驾
公共字符串descripe(){
返回“蛋糕”+id;
}
}
抽象类装饰器实现Cake_{
保护蛋糕;
公共装饰师(蛋糕){
这个蛋糕=蛋糕;
}
@凌驾
公共字符串descripe(){
返回蛋糕。描述();
}
@凌驾
公共字符串toString(){
返回descripe();
}
}
类磨砂扩展装饰器{
公共糖霜(蛋糕){
超级(蛋糕);
}
@凌驾
公共字符串descripe(){
返回蛋糕。描述()+“磨砂”;
}
}
类装饰器{
公共装饰(蛋糕){
超级(蛋糕);
}
@凌驾
公共字符串descripe(){
返回蛋糕。descripe()+“装饰”;
}
}
//对于蛋糕制造装配线:
类CreateSupplier{
私有int-id;
公共创建蛋糕(int-id){
this.id=id;
}
@凌驾
公共蛋糕{
返回新蛋糕(id);
}
}
类实现函数{
@凌驾
公共霜敷(蛋糕){
返回新的磨砂(蛋糕);
}
}
类decorecakes实现消费者{
公开结果;
@凌驾
公共无效接受(霜状fc){
结果=新装饰(fc);
}
}
公开课考试{
公共静态int NUM OF_=20;
公共静态void main(字符串[]args){
//更改默认的线程数:
System.setProperty(
“java.util.concurrent.ForkJoinPool”+
“.common.parallelism”、“+NUM_OF_饼”;
//测试/演示装饰图案:
装饰清单=
IntStream.range(0,蛋糕的数量)
.mapToObj(蛋糕::新)
.map(磨砂::新建)
.map(装饰::新建)
.collect(Collectors.toList());
forEach(System.out::println);
//并行构建蛋糕:

列表对您的问题的快速回答是,
thenApply
行没有编译,因为上面这行(
map(CompletableFuture::supplyAsync)
)的结果返回
Stream
,而不是
CompletableFuture
。 您需要执行类似于
map(cakeFuture->cakeFuture.thenappy(new FrostCakes())
的操作

但我认为还有更重要的一点需要说明

如果您的示例是出于教育目的,我建议您再花一两天时间准备,更具体地说,是阅读流操作的基础知识和
CompletableFuture

这样,当你开始展示你的材料时,你会感到更加自信,但更重要的是,你不会展示不完美的代码示例,这可能会损害你的同事/学生关于如何使用流和
CompletableFuture
s(甚至decorators)的概念

我会在你们的例子中指出一些我认为需要重做的事情

  • 手动设置公共
    ForkJoinPool
    的并行级别并不总是一个好主意。默认情况下,它使用运行时返回的处理器数量。availableProcessors()
  • 这是一个非常好的默认值。您需要有一个非常好的理由将其更改为其他值,因为在大多数情况下,您只会从调度和维护冗余线程中引入不必要的开销。而将其更改为您计划启动的任务数几乎总是一个坏主意(解释省略)
  • 您的流示例执行两个流操作,然后以集合终止,然后在集合列表上执行流操作。可以在没有集合到列表的情况下重写它们,方法是直接在上一次映射返回的流上应用
    forEach
    ,可以说这将是一个更好的演示使用Java8流对fluent编程模型进行优化
  • 您的示例也不会并行执行它们的操作。您可以通过在
    IntStream.range()之后添加
    .parallel()
    来轻松解决此问题
    ,但除非您从上面删除要列出的冗余集合,否则仅通过使用
    forEach
    打印列表内容,您将无法看到您已经并行完成了一些操作
  • 实现
    java.util.function
    接口的类不是很惯用。我认为应该用相应的lambda表达式替换它们,即使在您的例子中,这可能会导致lambda中出现大量lambda,除非第二个示例稍微重写
  • CompletableFuture。然后accept
    返回一个
    CompletableFuture
    ,因此在流处理的这一阶段,您会丢失对已创建、磨砂和装饰蛋糕的引用。如果您不关心它们,或者如果您在装饰逻辑中记录了一些关于它们的信息,这是可以的,但您的示例很容易误导人们最后收集的
    CompletableFuture
    s列表可以用来获取蛋糕(毕竟,谁不想既有蛋糕又吃蛋糕)
  • 因此,您的第一个示例可以如下所示

    IntStream.range(0, NUM_OF_CAKES)
            .parallel()
            .mapToObj(Cake::new)
            .map(Frosted::new)
            .map(Decorated::new)
            .forEach(System.out::println);
    

    请注意,由于并行执行,蛋糕ID没有排序。

    当询问编译错误时,请发布编译错误。您正在对流调用thenApply()。流没有任何thenApply()方法。
    IntStream.range(0, NUM_OF_CAKES)
            .parallel()
            .mapToObj(Cake::new)
            .map(Frosted::new)
            .map(Decorated::new)
            .forEach(System.out::println);