Java 有一个类似于 Thread Building Blocks 的并行任务框架——它被称为 Fork-Join 框架。它是available,用于当前的 Java SE 6 并包含在即将推出的 Java SE 7 中。
除了 javadoc 类文档之外,还有一些资源可用于开始使用该框架。来自jsr166 page,提到
“还有一个 wiki,其中包含这些课程的其他文档、注释、建议、示例等。”
fork-join examples,比如矩阵乘法是一个很好的起点。
我在解决一些Intel's 2009 threading challenges 时使用了fork-join 框架。该框架是轻量级和低开销的——我的是骑士巡回赛问题的唯一 Java 条目,它在比赛中的表现优于其他条目。 Java 源代码和文章可从挑战网站下载。
编辑:
我不知道一个类或部分如何
的代码可能会在将其推送到
一个游泳池 [...]
您可以通过继承ForKJoinTask 子类之一来创建自己的任务,例如RecursiveTask。这是并行计算斐波那契数列的方法。 (取自RecursiveTask javadocs - cmets 是我的。)
// declare a new task, that itself spawns subtasks.
// The task returns an Integer result.
class Fibonacci extends RecursiveTask<Integer> {
final int n; // the n'th number in the fibonacci sequence to compute
Fibonnaci(int n) { this.n = n; } // constructor
Integer compute() { // this method is the main work of the task
if (n <= 1) // 1 or 0, base case to end recursion
return n;
Fibonacci f1 = new Fibonacci(n - 1); // create a new task to compute n-1
f1.fork(); // schedule to run asynchronously
Fibonacci f2 = new Fibonacci(n - 2); // create a new task to compute n-2
return f2.invoke() + f1.join(); // wait for both tasks to compute.
// f2 is run as part of this task, f1 runs asynchronously.
// (you could create two separate tasks and wait for them both, but running
// f2 as part of this task is a little more efficient.
}
}
然后你运行这个任务并得到结果
// default parallelism is number of cores
ForkJoinPool pool = new ForkJoinPool();
Fibonacci f = new Fibonacci(100);
int result = pool.invoke(f);
这是一个简单的例子。在实践中,性能不会那么好,因为与任务框架的开销相比,任务执行的工作是微不足道的。根据经验,一个任务应该执行一些重要的计算 - 足以使框架开销微不足道,但又不会导致在运行一项大型任务的问题结束时有一个内核。将大型任务拆分为较小的任务可确保一个内核不会在其他内核空闲时做大量工作 - 使用较小的任务会使更多内核保持忙碌,但不会小到该任务没有实际工作。
[...] 或者什么时候代码看起来很奇怪
您需要复制所有内容
以及推动了多少
到游泳池上。
只有任务本身被推送到池中。理想情况下,您不想复制任何内容:为了避免干扰和锁定的需要,这会减慢您的程序,理想情况下,您的任务应该使用独立数据。只读数据可以在所有任务之间共享,不需要复制。如果线程需要合作构建一些大型数据结构,最好将它们单独构建,然后在最后将它们组合起来。组合可以作为一个单独的任务完成,或者每个任务都可以将它的一部分添加到整体解决方案中。这通常确实需要某种形式的锁定,但如果任务的工作量远大于更新解决方案的工作量,这不是一个相当大的性能问题。 My Knight's Tour 解决方案采用这种方法来更新板上的通用旅行存储库。
与常规单线程编程相比,处理任务和并发性是一个相当大的范式转变。通常有几种设计可以解决给定的问题,但只有其中一些适合线程解决方案。可能需要几次尝试才能了解如何以多线程方式重铸熟悉的问题。最好的学习方法是查看示例,然后自己尝试。始终分析和测量不同线程数的影响。您可以在池构造函数中显式设置要在池中使用的线程(核心)数。当任务被线性分解时,随着线程数量的增加,您可以预期接近线性的加速。