gpt4 book ai didi

Java 发生在关系 invokeAndWait 之前

转载 作者:行者123 更新时间:2023-12-03 12:46:27 28 4
gpt4 key购买 nike

我的问题与this有关问题,已经有了答案:

yes, there is a happens-before relationship imposed between actionsof the thread calling invokeLater/invokeAndWait and actions on theEDT of the runnable thereby submitted.


我的问题更笼统一点:是否甚至可以实现一种方法,例如 invokeAndWait ,这样它才能正常工作,但不会强加发生在之前的关系?通过正常工作的方法,我的意思如下:
  • 已提交 Runnable保证只执行一次。
  • 已提交 Runnable在特定线程上执行。
  • 该方法等待直到执行提交的Runnable已完成。
  • 该方法保证在提交的Runnable执行后返回已完成。

  • 对我来说似乎没有办法在不强加发生之前的关系的情况下实现这一点,还是我错了?如果是这样,请包含一个示例实现,以证明这一点。

    最佳答案

    这里最难的要求是:

    The submitted Runnable is guaranteed to be executed exactly once.


    使用非 volatile (Plain) 字段将工作任务从提交者转移到执行者不会创建发生之前的关系,但也不能保证执行者完全或在有限的时间内看到任务。编译器将能够优化对该字段的分配,或者在运行时执行程序线程可能只从其缓存而不是从主内存中读取值。
    因此,对于使用 Java 8 或更低版本的代码,我会说答案是“不,这样的 invokeAndWait 方法是不可能的”(除非可能使用 native 代码)。
    但是,Java 9 添加了内存模式 Opaque。 Using JDK 9 Memory Order Modes(添加了此功能)的作者 Doug Lea 的页面“ JEP 193”非常详细地描述了这一点。最重要的是,不透明模式比 volatile 弱,但仍提供以下保证:
    • Progress. Writes are eventually visible.
      [...]
      For example in constructions in which the only modification of some variable x is for one thread to write in Opaque (or stronger) mode, X.setOpaque(this, 1), any other thread spinning in while(X.getOpaque(this)!=1){} will eventually terminate.
      [...]
      Note that this guarantee does NOT hold in Plain mode, in which spin loops may (and usually do) infinitely loop [...]

    在设计这种没有发生之前关系的 invokeAndWait 方法时,您还必须考虑启动线程之前的操作发生在该线程 ( JLS §17.4.4 ) 中的第一个操作之前。所以工作线程必须在构造 Action 之前启动。
    此外,必须考虑“ final 字段语义”( JLS §17.15.1 )。当 invokeAndWait 的调用者以 lambda 表达式的形式创建 Runnable 时,该 lambda 对变量的捕获具有(据我所知)隐式 final 字段语义。

    If so, please include an example implementation, which proves this.


    由于硬件和时间相关,使用示例证明或反驳线程安全或发生在关系之前是困难的,如果不是不可能的话。但是,像 jcstress 这样的工具可以帮助解决这个问题。
    下面是一个没有发生之前关系的 invokeAndWait 的(简化的)潜在实现。请注意,我并不完全熟悉 Java 内存模型,因此代码中可能存在错误。
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.atomic.AtomicReference;

    class OpaqueExecutor {
    // For simplicity assume there will every only be a single work task
    // So pending task being replaced by other task is not an issue
    private final AtomicReference<Runnable> nextTask = new AtomicReference<>();

    public OpaqueExecutor() {
    Thread worker = new Thread(() -> {
    while (true) {
    // Use getOpaque() to no create happens-before relationship
    Runnable task = nextTask.getOpaque();
    if (task == null) {
    // For efficiency indicate to the JVM that this is busy-waiting
    Thread.onSpinWait();
    } else {
    // Clear pending task; memory mode here does not matter because we only want
    // to guarantee that this thread does not see task again
    nextTask.setPlain(null);
    task.run();
    }
    }
    }, "Worker thread");
    worker.setDaemon(true);
    worker.start();
    }

    public void invokeLater(Runnable runnable) {
    // For simplicity assume that there is no existing pending task which could be
    // replaced by this
    // Use setOpaque(...) to not create happens-before relationship
    nextTask.setOpaque(runnable);
    }

    private static class Task implements Runnable {
    private final AtomicBoolean isFinished = new AtomicBoolean(false);
    // Must NOT be final to prevent happens-before relationship from
    // final field semantics
    private Runnable runnable;

    public Task(Runnable runnable) {
    this.runnable = runnable;
    }

    public void run() {
    try {
    runnable.run();
    } finally {
    // Use setOpaque(...) to not create happens-before relationship
    isFinished.setOpaque(true);
    }
    }

    public void join() {
    // Use getOpaque() to no create happens-before relationship
    while (!isFinished.getOpaque()) {
    // For efficiency indicate to the JVM that this is busy-waiting
    Thread.onSpinWait();
    }
    }
    }

    public void invokeAndWait(Runnable runnable) {
    Task task = new Task(runnable);
    invokeLater(task);
    task.join();
    }

    public static void main(String... args) {
    // Create executor as first step to not create happens-before relationship
    // for Thread.start()
    OpaqueExecutor executor = new OpaqueExecutor();

    final int expectedValue = 123;
    final int expectedNewValue = 456;

    class MyTask implements Runnable {
    // Must NOT be final to prevent happens-before relationship from
    // final field semantics
    int value;

    public MyTask(int value) {
    this.value = value;
    }

    public void run() {
    int valueL = value;
    if (valueL == expectedValue) {
    System.out.println("Found expected value");
    } else {
    System.out.println("Unexpected value: " + valueL);
    }

    value = expectedNewValue;
    }
    }

    MyTask task = new MyTask(expectedValue);
    executor.invokeAndWait(task);

    int newValue = task.value;
    if (newValue == expectedNewValue) {
    System.out.println("Found expected new value");
    } else {
    System.out.println("Unexpected new value: " + newValue);
    }
    }
    }

    关于Java 发生在关系 invokeAndWait 之前,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64497416/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com