当前位置: 首页 > 知识库问答 >
问题:

在连续的java CompletionStages之间对共享上下文对象所做的最新更改对执行lambda的每个线程始终可见

邹英发
2023-03-14
package org.stackoverflow.example;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MeetPlay {

    private static final class Context {
        List<String> data = new ArrayList<>();
    }

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(
                Runtime.getRuntime().availableProcessors());

        Context context = new Context();
        CompletableFuture.completedFuture(context)
                .thenAcceptAsync(c -> context.data.add("1"), executor)
                .thenAcceptAsync(__ -> context.data.add("2"), executor)
                .thenAcceptAsync(__ -> {
                    if (context.data.contains("1") && context.data.contains("2")) {
                        System.out.println("Will that always be the case");
                    } else {
                        System.out.println("Or not");
                    }
                }, executor);
    }
}

我目前的理解是,上面的代码将始终打印:“无论执行器使用多少线程、涉及多少阶段和项目,或者Context对象有多“复杂”(例如,具有更多嵌套字段),因为在java完成阶段之间发生之前的保证。

工作中的一位同事辩称,这并不能保证,并表示他已经从经验上证明了这一点,然而,我实际上并没有亲眼看到这一点。在那之前,我想知道你们是怎么想的,最重要的是为什么。非常感谢您的推荐!

编辑:问题是关于共享上下文周围的内存可见性保证,而不是每个阶段是否在前一个阶段完成后执行。

共有1个答案

宰父嘉胜
2023-03-14

你是对的-这些阶段是按顺序执行的。这可以通过运行以下代码来证明:

    CompletableFuture.completedFuture(context)
        .thenAcceptAsync(c -> {
            context.data.add("1");
            System.out.println("1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
            }, executor)
        .thenAcceptAsync(__ -> {
            context.data.add("2");
            System.out.println("2");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
        }, executor)
        .thenAcceptAsync(__ -> {
            if (context.data.contains("1") && context.data.contains("2")) {
                System.out.println("Will that always be the case");
            } else {
                System.out.println("Or not");
            }
        }, executor);

结果将是:

1
(pause 1 sec)
2
(pause 1 sec)
Will that always be the case

发生这种情况是因为从thenAcceptAsync返回的CompletionStage仅在操作完成后完成。也就是说,如果操作异常完成,那么CompletionStage也会完成。此行为与thenAccep相同。ThenAcceptAsync中的“Async”仅表示操作将使用另一个执行器执行

如果需要并行执行,请考虑以下代码:

    CompletableFuture<Context> f = CompletableFuture.completedFuture(context);
    f.thenAcceptAsync(c -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        context.data.add("1");
        System.out.println("1");
    }, executor);
    f.thenAcceptAsync(__ -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        context.data.add("2");
        System.out.println("2");
    }, executor);
    f.thenAcceptAsync(__ -> {
        if (context.data.contains("1") && context.data.contains("2")) {
            System.out.println("Will that always be the case");
        } else {
            System.out.println("Or not");
        }
    }, executor);

在这里,3个阶段添加到同一阶段,而不是一个接一个。一个阶段的完成和另一个阶段的开始之间没有依赖关系。因此阶段可以并行执行。结果将是:

Or not
(pause 1 sec)
2
1
 类似资料:
  • 问题内容: 我正在使用Python模块(PyCLIPS)和Django 1.3。 我想开发一个线程安全类,该类可实现对象池和Singleton模式,并且还必须在Django中的请求之间共享。 例如,我要执行以下操作: 请求从池中获取具有某些ID的对象,对其进行处理,然后将其推回池中,然后发送带有对象ID的响应。 另一个具有对象ID的请求从池中获取具有给定ID的对象,并重复上述请求中的步骤。 但是对

  • 问题内容: 通过回答此问题,可以回答所有其他三个问题。希望我能说清楚: 在通过多处理创建的某些过程中创建对象后: 如何将对该对象的 引用 传递给其他进程? (不是很重要)我如何确保持有参考书时此过程不会消失? 示例1(已解决) 例子2 假设返回一个具有可变状态的对象。这个相同的对象应该可以从其他进程访问。 例子3 我有一个带有打开文件和锁的对象-如何授予对其他进程的访问权限? 提醒 我不希望此特定

  • 我有一个节点类,它包含两个嵌套类,服务器和客户机,以及指向每个类实例的指针。 我的猜测是上下文切换正在发生,因此每个进程都有自己的副本被加载到相同的地址中。 我如何改变这一点,使每个进程访问该节点的完全相同的副本? 更新:

  • 问题内容: 我对进程之间具有文件句柄的共享资源有疑问。这是我的测试代码: 然后我得到如下图所示: 我认为当我将对象放入队列时,对象被序列化了,而文件句柄无法序列化,所以,我得到了: 有人对此有任何想法吗?如果要共享具有文件句柄属性的对象,该怎么办? 问题答案: 我不得不反对(总之,不仅仅会放在注释中;-) @Mark反复断言文件句柄不能“在运行的进程之间传递”-这在现实,现代中根本不是真的操作系统

  • 我是Java新手,我想为每个现有和每个新对象更改一个类变量,而无需遍历每个对象。 这里的class变量是handlingFee,我在另一个类中有一个集合(ArrayList)来存储这些类对象。这有可能吗? 那么你能告诉我如何改变这个值吗?我必须迭代每个对象吗?

  • 我创建了一个可运行的类a,它为我执行一些任务。我使用ExecutorService提交这个类,以便并行执行这些任务。 可运行类A调用另一个对象B,该对象发送一个AsyncFuture请求(future.get()one)。 我将可运行类A的单独对象提交给ExecutorService,但是,类B的对象由bean(单例)引用。这会导致线程执行出现问题吗? 我注意到类A的一些对象没有被任何线程执行。