Fork-join中的内存可见性

Jos*_*man 6 java scala fork-join jsr166

Brian Goetz在http://www.ibm.com/developerworks/java/library/j-jtp03048.html上写了一篇关于fork-join的好文章.在其中,他列出了使用fork-join机制的合并排序算法,在该机制中,他并行执行数组两侧的排序,然后合并结果.

该算法同时对同一阵列的两个不同部分进行排序.为什么不是AtomicIntegerArray或维持可见性所需的其他机制?什么保证一个线程会看到另一个线程完成的写入,或者这是一个微妙的错误?作为后续跟进,Scala的ForkJoinScheduler是否也提供此保证?

谢谢!

Joh*_*int 6

(ForkJoin)的连接本身需要一个同步点,这是最重要的信息.同步点将确保在所述点之后发生的所有写入都是可见的.

如果您查看代码,可以看到同步点出现的位置.这只是调用invokeAll的一个方法

public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
    t2.fork();
    t1.invoke();
    t2.join();
}
Run Code Online (Sandbox Code Playgroud)

这里t2分叉到另一个进程,t1执行它的任务,并且调用线程将在t2.join()上等待.通过t2时.然后,对t1和t2的所有写入都将可见.

编辑:这个编辑只是为了解释我对同步点的意义.

可以说你有两个变量

int x;
volatile int y;
Run Code Online (Sandbox Code Playgroud)

任何时候你写y你读之前发生的所有写作都将可用.例如

public void doWork(){
   x = 10;
   y = 5;
}
Run Code Online (Sandbox Code Playgroud)

如果另一个线程读取y = 5,则保证线程读取x = 10.这是因为写入y会创建一个同步点,在该点之后,在写入之后所有写入之前的所有写入都将可见.

使用Fork Join池,ForkJoinTask的连接将创建一个同步点.现在,如果t2.fork()和t1.invoke(),t2的加入将确保将看到之前发生的所有写入.由于所有先前的写入都在相同的结构内,因此可见性是安全的.

如果不清楚,我会很乐意进一步解释.