Java 线程池交互的内存模型 happens-before 保证

Java Memory Model happens-before guarantees for Thread Pool interactions

Java 内存模型是否为线程池交互提供 happens-before 保证?特别是,将在 运行 工作队列中的一个项目结束之前由线程池工作线程进行的写入对工作线程 运行 宁下一个工作队列中的项目可见在那之后排队?

规范(我个人认为这个 FAQ 很有用:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization)指出“线程上对 start() 的调用发生在启动线程中的任何操作之前。" 或者简单地说,您在启动线程之前所做的任何内存写入都将在启动线程将要执行的 运行() 方法之前执行并对其可见。对于线程池来说是不同的,在你写之前 start() 通常会 运行。考虑一个简单的工作流,其中上下文对象发生变化并传递给下一个操作:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

    private static class Container<T> {
        private T value;
        public T get() {
            return value;
        }
        public void set(T newValue) {
            value = newValue;
        }
    }

    public static void main(String[] args) {
        final Container<Integer> sharedObject = new Container<>();
        final ExecutorService executor = Executors.newFixedThreadPool(10);
        // SKIPPED: pre-warm the executor so all worker threads are start()'ed
        final Runnable read = () -> System.out.println("Got " + sharedObject.get());
        Runnable write = () -> {
            sharedObject.set(35);
            executor.execute(read);
        };
        executor.execute(write);
        // SKIPPED: wait until done
    }
}

write.run()sharedObject.value 的写入是否保证对 read.run() 可见(不询问排序,这是显而易见的)?

(PS: 我理解 value volatile 确实提供了这种保证)

更新(补充答案): java.util.concurrent 的包摘要文档总结了语言提供并由框架扩展的内存一致性保证:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

引用 ExecutorService 的 javadoc:

Memory consistency effects: Actions in a thread prior to the submission of a Runnable or Callable task to an ExecutorService happen-before any actions taken by that task, which in turn happen-before the result is retrieved via Future.get().

但是,它没有说明添加到队列中的两个任务,以及任务 1 的处理是否发生在任务 2 的处理之前,从任务来看。只是将任务添加到队列发生在任务处理它之前,并且任务执行发生在原始调用者检索结果之前。

更新

两个不同的、独立提交的任务之间没有 happens-before 相关性,即使以某种方式知道一个 运行 在另一个开始之前 运行 完成。

当然,当一个任务提交另一个任务时,正如问题中所做的那样,在任务 1 中采取的任何操作在它提交任务 2 之前, happen-before 执行任务 2.

如果任务 1 在提交任务 2 后继续做其他事情,当然没有 happen-before 保证,因为任务 2 可能 运行 并在任务 1 继续工作之前完成。

我认为保证可见。 ExecutorService 扩展了 ExecutorExecutorjavadocs 说:

Memory consistency effects: Actions in a thread prior to submitting a Runnable object to an Executor happen-before its execution begins, perhaps in another thread.

根据我的阅读,这与您的示例中发生的情况相符。 write runnable 正在提交 read runnable,因此在 write 线程中提交之前的事件之间存在 happens-before 关系(即set 调用)以及之后在 read 线程中发生的事件(即 get 调用)。

write runnable 本身被提交的事实意味着在 Container 对象的创建和致电 set.