要以 PDF 格式阅读此主题,请点击这里.

Fork/JoinDivide and Conquer 是一种解决分层问题的强大抽象。

抽象

在谈论分层问题时,请考虑快速排序、归并排序、文件系统或一般树导航等。

  • Fork/Join 算法实质上将手头的任务分解成多个较小的子任务,并递归地对每个子任务应用相同的算法。

  • 一旦子任务足够小,就可以直接解决。

  • 所有子任务的解决方案被组合起来以解决它们的父任务,这反过来有助于解决其自身的父任务。

JSR-166y 库存在一些粗糙的边缘,可能会伤害您。

JSR-166y 库非常完美地为我们解决了 Fork/Join 编排问题,但它还存在一些粗糙的边缘,如果您不够注意,可能会伤害您。您仍然需要处理线程、池和同步屏障。


GPars 抽象便利层

GPars 可以隐藏处理线程、池、屏障和 RecursiveActions 的复杂性,但仍可让您利用 jsr166y 中强大的 Fork/Join 实现。

ForkJoin 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import static groovyx.gpars.GParsPool.runForkJoin
import static groovyx.gpars.GParsPool.withPool

//feel free to experiment with the number of fork/join threads in the pool
withPool(1) {pool ->
    println """Number of files: ${
        runForkJoin(new File("./src")) {file ->
            long count = 0
            file.eachFile {
                if (it.isDirectory()) {
                    println "Forking a child task for $it"
                    forkOffChild(it)       //fork a child task
                } else {
                    count++
                }
            }
            return count + (childrenResults.sum(0))
            //use results of children tasks to calculate and store own result
        }
    }"""
}

Fork/Join 节约您的资源

由于内部使用 TaskBarrier 类来同步线程,因此 Fork/Join 操作可以在少量线程上安全运行。

当一个线程在算法内部被阻塞,等待其子任务被计算时,该线程会悄悄地返回到池中,以接管任务队列中任何可用的子任务并处理它们。虽然该算法会创建与子目录一样多的任务,并且任务会等待子目录任务完成,但只要一个线程就足以使计算持续进行,并最终计算出一个有效的结果。

如果您想了解更多信息,请查看 用户指南中的 _Fork/Join 部分.