繁体   English   中英

用java进行多线程求和

[英]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 ,然后这个线程会践踏该线程的更新。

您需要使用某种同步来确保更新以原子方式发生; 或者使用AtomicLongLongAdder类的东西,它们可以自动更新。

或者更好地使用 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM