java jsr
在未来几年中,我们将看到标准台式机,笔记本电脑和服务器计算机中的内核数量将不断增加。 原因很简单:添加额外的内核要比构建更快的单个处理器便宜。 因此,我们将不得不编写更多支持并发的软件以利用更好的硬件。
老实说,我不喜欢并发。 我的个人规则是“您需要一个充分的理由来实现并发,并且如果需要做的话必须非常小心。”在过去的几年中,我看到的错误实现多于工作。 这就是为什么我喜欢fork&join库的原因。 清晰的编程模型可实现样板代码,可防止您出错。 但是,如果您打算使用fork和join,请花一些时间来了解其行为。
文件#1和#2中的示例与Java 7文档中的示例代码非常相似。 通常,使用递归算法的斐波那契数不是一个好主意,因为存在更好的线性解决方案(请比较http://nayuki.eigenstate.org/page/fast-fibonacci-algorithms ),但它更易于实现和理解相对于其它的。 因此,让我们看一下示例:
// File #1: FibonacciTask.java [error handling, parameter validation and asserts removed] package com.sprunck.sample;import java.util.concurrent.RecursiveTask;public class FibonacciTask extends RecursiveTask<Long> {private static final long serialVersionUID = 1L;private final long inputValue;public FibonacciTask(long inputValue) {this.inputValue = inputValue;}@Overridepublic Long compute() {if (inputValue == 0L) {return 0L;} else if (inputValue <= 2L) {return 1L;} else {final FibonacciTask firstWorker = new FibonacciTask(inputValue - 1L);firstWorker.fork();final FibonacciTask secondWorker = new FibonacciTask(inputValue - 2L);return secondWorker.compute() + firstWorker.join();}}
}
// File #2: FibonacciTaskTest.java package com.sprunck.sample;import java.util.concurrent.ForkJoinPool;
import junit.framework.Assert;
import org.junit.Test;public class FibonacciTaskTest {// it makes no sense to create more threads than available cores (no speed improvement here)private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();// create thread poolprivate final ForkJoinPool pool = new ForkJoinPool(AVAILABLE_PROCESSORS);@Testpublic void testFibonacciArray() {// more test data: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.htmllong results[] = { 0L, 1L, 1L, 2L, 3L, 5L, 8L, 13L, 21L, 34L, 55L, 89L, 144L, 233L, 377L, 610L, 987L, 1597L,2584L, 4181L, 6765L };for (int inputValue = 0; inputValue < results.length; inputValue++) {final FibonacciTask task = new FibonacciTask(inputValue);System.out.print("Fibonacci(" + inputValue + ") = ");final long result = pool.invoke(task);System.out.println(result);Assert.assertEquals(results[inputValue], result);}}
}
// FibonacciTaskTest.java的输出
Fibonacci(0) = 0
Fibonacci(1) = 1
Fibonacci(2) = 1
Fibonacci(3) = 2
Fibonacci(4) = 3
Fibonacci(5) = 5
Fibonacci(6) = 8
Fibonacci(7) = 13
Fibonacci(8) = 21
Fibonacci(9) = 34
Fibonacci(10) = 55
Fibonacci(11) = 89
Fibonacci(12) = 144
Fibonacci(13) = 233
Fibonacci(14) = 377
Fibonacci(15) = 610
Fibonacci(16) = 987
Fibonacci(17) = 1597
Fibonacci(18) = 2584
Fibonacci(19) = 4181
Fibonacci(20) = 6765
到目前为止,这是一个简单明了的解决方案。 没有用于并行的样板代码,例如线程同步。
但我想鼓励您更深入地了解解决方案中发生的情况。 在文件#3和#4中,您可以找到同一程序的增强版本。 第一个版本和第二个版本之间的唯一区别是,一些代码可以跟踪执行过程中发生的事情,而较小的slowTask()可以模拟更实际的行为。
// File #3: FibonacciTaskTraces.java package com.sprunck.sample;import java.util.concurrent.RecursiveTask;public class FibonacciTaskTraces extends RecursiveTask<Long> {private static final long serialVersionUID = 1L;// just needed to format debug outputpublic static final String OUTPUT_PREFIX = " | ";private final String prefix;private final long inputValue;public FibonacciTaskTraces(long inputValue, final String prefix) {this.inputValue = inputValue;this.prefix = prefix;}@Overridepublic Long compute() {if (inputValue == 0L) {slowTask();return 0L;} else if (inputValue <= 2L) {slowTask();return 1L;} else {final long firstValue = inputValue - 1L;System.out.println(prefix + " - Fibonacci(" + firstValue + ") <- " + Thread.currentThread().getName()+ " (fork) ");final FibonacciTaskTraces firstWorker = new FibonacciTaskTraces(firstValue, prefix + OUTPUT_PREFIX);firstWorker.fork();final long secondValue = inputValue - 2L;System.out.println(prefix + " - Fibonacci(" + secondValue + ") <- " + Thread.currentThread().getName());final FibonacciTaskTraces secondWorker = new FibonacciTaskTraces(secondValue, prefix + OUTPUT_PREFIX);long result = secondWorker.compute() + firstWorker.join();System.out.println(prefix + " - Fibonacci(" + inputValue + ") = " + result + " <- "+ Thread.currentThread().getName() + " (join)");slowTask();return result;}}/** just simulate a longer running task (with out disturbing the other threads) */private void slowTask() {for (int k = 0, i = 0; i < 1000 * 1000 * 100; i++) {i = i + k;}}
}
// File #4: FibonacciTaskTracesTask.javapackage com.sprunck.sample;import java.util.concurrent.ForkJoinPool;
import junit.framework.Assert;
import org.junit.Test;public class FibonacciTaskTracesTest {// it makes no sense to create more threads than available cores (no speed improvement here)private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();// create thread poolprivate final ForkJoinPool pool = new ForkJoinPool(AVAILABLE_PROCESSORS);@Testpublic void testFibonacciArrayTraces() {// more test data: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.htmllong results[] = { 0L, 1L, 1L, 2L, 3L, 5L, 8L, 13L };for (int inputValue = 0; inputValue < results.length; inputValue++) {final FibonacciTaskTraces task = new FibonacciTaskTraces(inputValue, " | ");System.out.println("invoke Fibonacci(" + inputValue + ") <- " + Thread.currentThread().getName());final long result = pool.invoke(task);System.out.println("result = " + result + "\n");Assert.assertEquals(results[inputValue], result);}}
}
// FibonacciTaskTracesTest.java的输出
invoke Fibonacci(0) <- mainresult = 0invoke Fibonacci(1) <- mainresult = 1invoke Fibonacci(2) <- mainresult = 1invoke Fibonacci(3) <- main| - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(1) <- ForkJoinPool-1-worker-1| - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join)result = 2invoke Fibonacci(4) <- main| - Fibonacci(3) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(2) <- ForkJoinPool-1-worker-1| | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(1) <- ForkJoinPool-1-worker-2| | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join)| - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-1 (join)result = 3invoke Fibonacci(5) <- main| - Fibonacci(4) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(3) <- ForkJoinPool-1-worker-1| | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | - Fibonacci(1) <- ForkJoinPool-1-worker-1| | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(2) <- ForkJoinPool-1-worker-2| | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2| | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join)| | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join)| | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join)| - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-1 (join)result = 5invoke Fibonacci(6) <- main| - Fibonacci(5) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(4) <- ForkJoinPool-1-worker-1| | - Fibonacci(3) <- ForkJoinPool-1-worker-1 (fork) | | - Fibonacci(2) <- ForkJoinPool-1-worker-1| | - Fibonacci(4) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(3) <- ForkJoinPool-1-worker-2| | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2| | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1| | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join)| | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join)| | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2| | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-1 (join)| | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2| | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join)| | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join)| | - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-2 (join)| - Fibonacci(6) = 8 <- ForkJoinPool-1-worker-1 (join)result = 8invoke Fibonacci(7) <- main| - Fibonacci(6) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(5) <- ForkJoinPool-1-worker-1| | - Fibonacci(4) <- ForkJoinPool-1-worker-1 (fork) | | - Fibonacci(3) <- ForkJoinPool-1-worker-1| | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1| | - Fibonacci(5) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(4) <- ForkJoinPool-1-worker-2| | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2| | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2| | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join)| | | - Fibonacci(3) <- ForkJoinPool-1-worker-1 (fork) | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1| | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join)| | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1| | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join)| | | - Fibonacci(4) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(3) <- ForkJoinPool-1-worker-2| | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2| | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join)| | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-1 (join)| | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join)| | - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-1 (join)| | | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2| | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1| | | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join)| | | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join)| | | - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-2 (join)| | - Fibonacci(6) = 8 <- ForkJoinPool-1-worker-2 (join)| - Fibonacci(7) = 13 <- ForkJoinPool-1-worker-1 (join)result = 13
现在,输出使您可以更深入地了解程序的处理。 出现以下斐波纳契数计算方法:
- 前三个斐波那契数在主线程中处理,
- 下一个斐波那契数仅在一个新线程(ForkJoinPool-1-worker-1)中处理,并且
- 从第五个斐波纳契数开始,使用了两个线程(ForkJoinPool-1-worker-1和ForkJoinPool-1-worker-2)。
该算法效率低下,因为在处理中存在很多冗余操作(重新计算相同的斐波那契数)。 在现实生活中的应用中,您应该小心使用这种效率低下的算法。 一些痕迹有助于了解发生了什么。
推荐品
- 使用fork和join既简单又直接,但是要花一些时间来跟踪和理解您的实现。
- 有时实现同一算法的两个版本(一个用于分析,另一个用于生产)会有所帮助。
- 花一些时间来设计和理解更好的并发算法是一项不错的投资。
以此方式开发了概率计算器 ( 概率计算器演示– PCALC )。
参考: 如何在Java 7 – JSR 166并发实用程序中实现fork and join? 由我们的JCG合作伙伴 Markus Sprunck在Software Engineering Candies博客上获得。
翻译自: https://www.javacodegeeks.com/2012/04/fork-and-join-in-java-7-jsr-166.html
java jsr