首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java发生-在关系invokeAndWait之前

Java发生-在关系invokeAndWait之前
EN

Stack Overflow用户
提问于 2020-10-23 09:26:42
回答 1查看 146关注 0票数 4

我的问题与问题有关,它已经有了一个答案:

是的,在调用invokeLater/invokeAndWait的线程的操作与由此提交的可运行的EDT上的操作之间强加关系之前,会发生这样的情况。

我的问题有点笼统:是否有可能实现一个方法,例如invokeAndWait,使其工作正常,但不强制在关系之前发生?这种方法正常工作,我指的是:

  • 提交的Runnable保证只执行一次。
  • 提交的Runnable在特定线程上执行。
  • 该方法等待提交的Runnable的执行完成。
  • 该方法保证在提交的Runnable执行完成后返回。

对我来说,似乎没有办法实现这一点而不强加一个发生-之前的关系,或我错了吗?如果是,请包括一个示例实现,这证明了这一点。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-10-31 23:08:37

这里最困难的要求是:

提交的Runnable保证只执行一次。

使用非volatile(平原)字段将工作任务从提交者转移到执行者不会在关系发生之前创建一个事件,但也不能保证执行者在任何时间内或在有限的时间内看到任务。编译器将能够优化对该字段的赋值,或者在运行时执行器线程可能只从缓存中读取值,而不是从主内存读取值。

因此,对于使用Java 8或更低版本的代码,我会说答案是“不可能,这样的invokeAndWait方法是不可能的”(除了使用本机代码外)。

然而,Java 9增加了内存模式的不透明。Doug的页面"使用JDK 9内存顺序模式“(添加了此功能)的作者Doug非常详细地描述了这一点。最重要的是,不透明模式比volatile弱,但仍然提供以下保证:

  • 进度。写入最终是可见的。 ..。 例如,在某些变量x的唯一修改是一个线程以不透明(或更强)模式写入的构造( X.setOpaque(this, 1) )中,任何在while(X.getOpaque(this)!=1){}中旋转的其他线程最终都将终止。 ..。 请注意,这种保证不适用于平原模式,在这种模式中,自旋环可以(而且通常是)无限循环.

在设计这样一个没有发生的invokeAndWait方法时--在关系发生之前--您还必须考虑启动线程之前的一个动作--在该线程中的第一个操作(JLS§17.4.4)之前。因此,必须在构造操作之前启动工作线程。

此外,还必须考虑"final字段语义“(JLS§17.15.1)。当invokeAndWait的调用者以lambda表达式的形式创建Runnable时,通过该lambda捕获变量具有(据我理解)隐含的final字段语义。

如果是,请包括一个示例实现,这证明了这一点。

在使用实例之前证明或否定线程安全或发生的关系是困难的,如果不是不可能的话,原因是硬件和时间依赖。然而,像强应力这样的工具可以在这方面有所帮助。

下面是一个(简化的) invokeAndWait的潜在实现,而不是发生之前的关系。请注意,我并不完全熟悉Java内存模型,因此代码中可能会出现错误。

代码语言:javascript
复制
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);
    }
  }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64497416

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档