java-8 CompletableFuture回调调用的不合理延迟

java-8 CompletableFuture回调调用的不合理延迟,java,multithreading,java-8,completable-future,Java,Multithreading,Java 8,Completable Future,第一次回叫相当快,其余的都延迟了50毫秒(非累加),我不知道为什么 public class CfTest { final static long t0 = System.nanoTime(); public static void main(String[] args) { CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(()->{sleep(2000); ret

第一次回叫相当快,其余的都延迟了50毫秒(非累加),我不知道为什么

public class CfTest {

    final static long t0 = System.nanoTime();

    public static void main(String[] args) {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(()->{sleep(2000); return 100;});
        CompletableFuture<Long> cf2 = CompletableFuture.supplyAsync(()->{sleep(1000); return 1L;});
        CompletableFuture<Long> cfs = cf1.thenCombine(cf2, (a,b)->a+b);
        cfs.thenAccept(x->debug("a1. " + x)); // Async process, sync when complete
        cfs.thenAccept(x->debug("a2. " + x)); // Async process, sync when complete
        cfs.thenAcceptAsync(x->debug("a3. " + x)); // Async process, async after complete too
        debug("b. " + cfs.join()); // Wait and process
        debug("c. " + cfs.join()); // Wait and process
    }

    private static void sleep(int i) {
        try {
            Thread.sleep(i);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static void debug(Object res) {
        System.out.println(String.format("after %.3fs: %s", (System.nanoTime()-t0) / (float)TimeUnit.SECONDS.toNanos(1), res));
        System.out.flush();
    }

}
编辑:其他让我更加惊讶的案例。我认为完整的未来可能不是问题。

如果我添加这一行:

    ...
    cfs.thenAcceptAsync(x->debug("a3. " + x)); // Async process, async after complete too
    System.out.println("Waiting..."); // <-- New Line
    debug("b. " + cfs.join()); // Wait and process
    debug("c. " + cfs.join()); // Wait and process
但如果我加上这一行:

    ...
    cfs.thenAcceptAsync(x->debug("a3. " + x)); // Async process, async after complete too
    debug("Waiting..."); // <-- New Line
    debug("b. " + cfs.join()); // Wait and process
    debug("c. " + cfs.join()); // Wait and process

如果我在join之前添加这一行,则延迟消失,因此延迟必须与类加载有关String.format初始化:

    ...
    cfs.thenAcceptAsync(x->debug("a3. " + x)); // Async process, async after complete too
    String.format("test"); // <-- Delay is gone with this line too
    debug("b. " + cfs.join()); // Wait and process
    debug("c. " + cfs.join()); // Wait and process

另一件奇怪的事情:如果在
debug()
方法的开头添加更多的打印输出,例如多个
System.out.format(“在%.3fs:%n之后,”(System.nanoTime()-t0)/(float)TimeUnit.SECONDS.toNanos(1))
,只有第一个打印输出显示更快的时间,后续的打印输出会有额外的延迟。此外-如果您在
调试中更改
System.out.println
以仅附加到字符串列表中,您仍然会得到延迟。我得到了答案,与并发性/线程甚至IO无关,这是类加载。我会发布的。这可能是由于内部正在解析和缓存字符串格式。如果您只需多次调用
System.out.println(String.format(…)
行,则只有在第一次调用和第二次调用之间才会有延迟。
System.out.flush()
已过时,
println
意味着刷新。哦,为什么要使用
println(String.format(…)
而不是
printf(…)
?扭曲的想法?我会记得在小基准测试之前“热身”。抱歉,这个问题有误。使用
-verbose:class
可以确认这一点:
2.054s:[从C:\Program Files\java\jdk1.8.0\u 45\jre\lib\rt.jar]a1加载了java.util.Formattable。101在2.085s后:a2。101在2.086s之后:a3。101在2.086s之后:b。101在2.086s之后:c。101
    ...
    cfs.thenAcceptAsync(x->debug("a3. " + x)); // Async process, async after complete too
    debug("Waiting..."); // <-- New Line
    debug("b. " + cfs.join()); // Wait and process
    debug("c. " + cfs.join()); // Wait and process
after 0,068s: Waiting...
after 2,066s: a1. 101
after 2,067s: a2. 101
after 2,067s: b. 101
after 2,067s: a3. 101
after 2,068s: c. 101
    ...
    cfs.thenAcceptAsync(x->debug("a3. " + x)); // Async process, async after complete too
    String.format("test"); // <-- Delay is gone with this line too
    debug("b. " + cfs.join()); // Wait and process
    debug("c. " + cfs.join()); // Wait and process
after 2,057s: a1. 101
after 2,068s: a2. 101
after 2,068s: b. 101
after 2,068s: a3. 101
after 2,069s: c. 101