Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/382.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么ThenComposeSync要等待回报才能赎回_Java_Java 8_Completable Future - Fatal编程技术网

Java 为什么ThenComposeSync要等待回报才能赎回

Java 为什么ThenComposeSync要等待回报才能赎回,java,java-8,completable-future,Java,Java 8,Completable Future,我已经编写了一个人为的代码示例,它可能不是某个人应该使用的代码,但我相信它应该工作。然而,它却陷入了僵局。我已经阅读了描述的答案,但发现它们不够充分 下面是代码示例: import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class Test { public static vo

我已经编写了一个人为的代码示例,它可能不是某个人应该使用的代码,但我相信它应该工作。然而,它却陷入了僵局。我已经阅读了描述的答案,但发现它们不够充分

下面是代码示例:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class Test {

    public static void main(String argv[]) throws Exception {
        
        int nThreads = 1;
        Executor executor = Executors.newFixedThreadPool( nThreads );
        

        
        CompletableFuture.completedFuture(true)
            .thenComposeAsync((unused)->{
            
                System.err.println("About to enqueue task");
                CompletableFuture<Boolean> innerFuture = new CompletableFuture<>();
                executor.execute(() -> {

                    // pretend this is some really expensive computation done asynchronously

                    System.err.println("Inner task");
                    innerFuture.complete(true);
                });
                System.err.println("Task enqueued");
                             
                return innerFuture;
            }, executor).get();

        System.err.println("All done");
        System.exit(0);

    }
    
}
import java.util.concurrent.CompletableFuture;
导入java.util.concurrent.Executor;
导入java.util.concurrent.Executors;
公开课考试{
公共静态void main(字符串argv[])引发异常{
int=1;
Executor Executor=Executors.newFixedThreadPool(nThreads);
CompletableFuture.completedFuture(真)
.ThenComposeSync((未使用)->{
System.err.println(“即将排队任务”);
CompletableFuture innerFuture=新的CompletableFuture();
执行者。执行(()->{
//假设这是一个非常昂贵的异步计算
System.err.println(“内部任务”);
innerFuture.complete(true);
});
System.err.println(“任务排队”);
回归未来;
},执行器)。get();
系统错误打印项次(“全部完成”);
系统出口(0);
}
}
这张照片是:

即将使任务排队

任务排队

然后它就挂了。它处于死锁状态,因为执行器只有一个线程,它正在等待内部未来变得可赎回。为什么“ThenComposeSync”阻止其返回值成为可赎回的,而不是返回仍然不完整的未来并释放其在执行器中的线程


这感觉完全不直观,javadocs也没有真正的帮助。我从根本上误解了完成阶段的工作方式吗?或者这是实现中的一个bug?

首先,让我用两个静态函数重写您的代码,以便更容易看到发生了什么:

// Make an executor equivalent to Executors.newFixedThreadPool(nThreads)
// that will trace to standard error when a task begins or ends
static ExecutorService loggingExecutor(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>()) {

                @Override
                protected void beforeExecute(Thread t, Runnable r) {
                    System.err.println("Executor beginning task on thread: " 
                       + t.getName());
                }

                @Override
                protected void afterExecute(Runnable r, Throwable t) {
                    System.err.println("Executor finishing task on thread: "
                       + Thread.currentThread().getName());
                }

            };
}
让我们测试您的结论,即在计算第二个未来的结果之前,不会释放第一个线程:

ExecutorService e = loggingExecutor(2);  // use 2 threads this time

CompletableFuture.completedFuture(true)
        .thenComposeAsync(inner(e), e)
        .join();

e.shutdown();

/*
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
Executor beginning task on thread: pool-1-thread-2
pool-1-thread-2: Inner task
Executor finishing task on thread: pool-1-thread-2
Executor finishing task on thread: pool-1-thread-1
*/
实际上,线程1似乎一直保持到线程2完成为止

让我们看看
thencoseasync
本身是否阻止了以下内容:

ExecutorService e = loggingExecutor(1);

CompletableFuture<Boolean> future = 
        CompletableFuture.completedFuture(true)
        .thenComposeAsync(inner(e), e);

System.err.println("thenComposeAsync returned");

future.join();

e.shutdown();

/*
thenComposeAsync returned
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
*/
如您所见,.thenRun(…)已在线程1上执行。我相信这与CompletableFuture的其他*Async(…,Executor-exec)方法是一致的

但是,如果您想将
thencomposesync
的功能分为两个单独可控的步骤,而不是让API来处理线程,该怎么办?您可以这样做:

ExecutorService e = loggingExecutor(1);

completedFuture(true)
        .thenApplyAsync(inner(e), e) // do the async part first
        .thenCompose(x -> x)         // compose separately
        .thenRun(() -> System.err.println(Thread.currentThread().getName()
                        + ": All done"))
        .join();

e.shutdown();
一切都将在1个线程上运行良好,没有死锁


总之,这种行为是否如你所说的那样不直观?我不知道。我无法想象为什么
thencoseasync
甚至存在。如果一个方法返回
CompletableFuture
,它不应该阻塞,也没有理由异步调用它

所以,在进行了很多有趣的对话之后,我决定给JDK的一位作者发电子邮件。发现这种行为不是故意的,实际上是1.8u25中存在的一个bug。有一个修补程序将与更高版本的Java 8一起发布。我不知道是哪个。对于想要测试新行为的任何人,可以在此处下载最新的jsr166 jar:


感谢米莎提供的细节和努力。你的例子比我的清楚得多。我需要考虑一下你的一些陈述。具体地说:“1.API需要等待Inner(e)返回CompletableFuture;2.它需要等待返回的CompletableFuture也完成。只有这样,未来才是完整的。因此,正如你所看到的,它不能按照你的建议返回不完整的未来。”我并不是在做“正如你所看到的……”所暗示的精神飞跃我会想一想,等我想好了再发回来。我不知道。不过,到现在为止,这个问题应该已经解决很久了。你看到了吗?我以为我看到了,但结果却是用户代码中一个无关的bug。无论如何,谢谢你。
ExecutorService e = loggingExecutor(1);

CompletableFuture<Boolean> future = 
        CompletableFuture.completedFuture(true)
        .thenComposeAsync(inner(e), e);

System.err.println("thenComposeAsync returned");

future.join();

e.shutdown();

/*
thenComposeAsync returned
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
*/
ExecutorService e = loggingExecutor(2);

CompletableFuture.completedFuture(true)
        .thenComposeAsync(inner(e), e)
        .thenRun(() -> System.err.println(Thread.currentThread().getName()
                         + ": All done"))
        .join();

e.shutdown();

/*
Executor beginning task on thread: pool-1-thread-1
pool-1-thread-1: About to enqueue task
pool-1-thread-1: Task enqueued
Executor beginning task on thread: pool-1-thread-2
pool-1-thread-2: Inner task
Executor finishing task on thread: pool-1-thread-2
pool-1-thread-1: All done
Executor finishing task on thread: pool-1-thread-1
*/
ExecutorService e = loggingExecutor(1);

completedFuture(true)
        .thenApplyAsync(inner(e), e) // do the async part first
        .thenCompose(x -> x)         // compose separately
        .thenRun(() -> System.err.println(Thread.currentThread().getName()
                        + ": All done"))
        .join();

e.shutdown();