[英]multithread summation with java
我打算使用多线程从 1 到 10000000 求和,下面的代码在这一行中无法正常工作 - sum = sum + Sum(finalI, finalI * step);
,总和在当前线程中始终为 0。 我已经添加了volatile
,我是 Java 新手我不知道如何解决这个问题。
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class testParallelFor {
volatile static long sum = 0;
final static int step = 100;
final static int start = 1;
final static int end = 10000000;
public static void main(String[] args) throws InterruptedException {
int num = Runtime.getRuntime().availableProcessors();
ExecutorService exec = Executors.newFixedThreadPool(num);
try {
for (int i = start; i < end; i = i * step) {
int finalI = i;
exec.submit(new Runnable() {
@Override
public void run() {
sum = sum + Sum(finalI, finalI * step);
}
});
}
} finally {
exec.shutdown();
exec.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
sum = sum + end;
System.out.println(sum);
}
}
static long Sum(int i, int j) {
System.out.println("Sum from " + i + " to " + j + " start.");
long temp = 0;
for (int k = i; k < j; k++) {
temp = temp + k;
}
System.out.println("Sum from " + i + " to " + j + " end. sum=" + temp);
return temp;
}
}
输出:
Sum from 1 to 100 start.
Sum from 1000000 to 100000000 start.
Sum from 100 to 10000 start.
Sum from 10000 to 1000000 start.
Sum from 1 to 100 end. sum=4950
Sum from 100 to 10000 end. sum=49990050
Sum from 10000 to 1000000 end. sum=499949505000
Sum from 1000000 to 100000000 end. sum=4999499950500000
4999499960500000
最简单的解决方案可能是使用并行流:
long start = 1;
long end = 10000000;
long result = LongStream.rangeClosed(start, end) // Creates a stream going from 1 to 10000000
.parallel() // Parallelize this stream
.sum(); // Sums every value of this stream
System.out.println(result);
这比你的版本清楚得多:)
当前线程中的总和始终为 0
由于表达式的求值顺序,这是可能的(虽然不能保证):
sum+ Sum(finalI, finalI * step)
sum
在调用Sum
之前被读取; 当需要大输入范围时, Sum
很慢。
所有线程大致同时启动,它们要做的第一件事就是读取sum
。 除非它已经被另一个线程更新,否则线程将读取初始值,评估Sum
,然后将结果添加回sum
变量,而不检查sum
是否在此期间更新。
您可以在阅读sum
之前对Sum
进行缓慢评估:
sum = Sum(finalI, finalI * step) + sum;
但是你仍然有一个问题,即sum
的读取sum
更新sum
的写入是原子地执行的:另一个线程可以在两者之间更新sum
,然后这个线程会践踏该线程的更新。
您需要使用某种同步来确保更新以原子方式发生; 或者使用AtomicLong
或LongAdder
类的东西,它们可以自动更新。
或者更好地使用 Executor,这样您就不必担心更新同步到 sum:
List<Future<Long>> futures = new ArrayList<>();
for (int i = start; i < end;i = i * step) {
int finalI = i;
futures.add(exec.submit(() -> Sum(finalI, finalI * step)));
}
long sum = 0; // No need to be volatile if you update in a single thread.
for (Future<Long> future : futures) {
sum += future.get();
}
或者使用并行流,如Abrikot's answer 。
当前线程中的总和始终为 0
不,这不是真的。 你可以替换ExecutorService exec = Executors.newFixedThreadPool(num);
与ExecutorService exec = Executors.newFixedThreadPool(1);
,您只会在第一个任务中看到sum
为 0。 这是因为只有一个线程,任务是顺序执行的,下一个任务会保证看到sum
的变化。
// replace `ExecutorService exec = Executors.newFixedThreadPool(num);` with `ExecutorService exec = Executors.newFixedThreadPool(1);`
// add `System.out.printf("i: %d, sum: %d\n", finalI, sum);` in `run`
i: 1, sum: 0
i: 100, sum: 4950
i: 10000, sum: 49995000
i: 1000000, sum: 499999500000
当线程数>=任务数时, sum
“永远”为0。这是因为任务可以并发执行,在其他线程更新sum
之前,它们看到的是初始的0。
但是当1<线程数<任务数时,你无法预测哪些任务会看到0,哪些任务会看到变化。 唯一的保证是当任务开始执行并开始读取sum
,它将看到最新的sum
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.