java - 同时求和的最佳方法

标签 java multithreading concurrency parallel-processing biginteger

我正在尝试计算一些大数字。为了加快计算速度,我想使用多线程。每个线程都要计算一个数,最后计算出和。

我曾经看到一些与 SumThreadCollector 一起工作的东西,如下所示:

public BigInteger compute(int p) {
    Collector c = new Collector(p);

    for(T element : Collection<T> bigCollection) {
        new SumThread(c) {

            @Override
            protected void doTheJob() {
                long big = someVeryComplexCalculation(element, ...); //n!
                receive(BigInteger.valueOf(big));
            }

        }
    }

    if(collector.isReady())
        return collector.getResult();

    return null;
}

public class Collector {

    private int numberOfProcesses;
    private int numberOfAllowedProcesses;
    private BigInteger result;

    public Collector(int n) {
        numberOfAllowedProcesses = n;
        numberOfProcesses = 0;
        result = BigInteger.ZERO;
    }

    synchronized public void enter() throws InterruptedException {
        if (numberOfProcesses == numberOfAllowedProcesses) wait();
        numberOfProcesses++;
    }

    synchronized public void leave() {
        numberOfProcesses--;
        notify();
    }

    synchronized public void register(BigInteger v) {
        result = result.add(v);
    }

    synchronized public boolean isReady() throws InterruptedException {
        while (numberOfProcesses > 0) wait();
        return true;
    }

    ...
}

public abstract class SumThread extends Thread {

    private Collector collector;

    public SumThread(Collector c) throws InterruptedException {
        collector = c;
        collector.enter();
    }

    abstract protected void doTheJob(); //complex calculations can be done in here

    public void receive(BigInteger t) {
        collector.register(t);
    }

    public void run() {
        doTheJob();
        collector.leave();
    }
}

我认为我可以通过使用 ExecutorService 轻松超越它而不是像这样不断地创建新的 Thread:

public BigInteger compute(int p) {
    ExecutorService pool = Executors.newFixedThreadPool(p);
    Future<BigInteger>[] futures = new Future<BigInteger>[bigCollection.size()];
    int i = 0;

    for(T element : Collection<T> bigCollection) {
        futures[i++] = p.submit(new Callable<BigInteger>() {

            @Override
            public BigInteger call() {
                long big = someVeryComplexCalculation(element, ...); //n!
                return BigInteger.valueOf(big);
            }

        }
    }

    // or with ExecutorCompletionService, but the loop remains I guess
    BigInteger res = BigInteger.ZERO
    for(Future<BigInteger> f : futures)
        res = res.add(f.get());

    return res;
}

不过,这段代码的性能并没有超过 SumThread-Collector 解决方案。例如,我还看到了有关 LongAdder 的内容,但我需要一些用于 BigInteger 的加法器...

因此我的问题是:同时计算总和的最佳方法是什么?是上述方法之一还是有完全不同(但更好)的方法?

最佳答案

正如您提到的在 Java-8 中添加的 LongAdder 并使用 effectively-final 变量,我假设您使用的是 Java-8。在此版本中,解决您的任务的最佳方法是使用 Stream API :

BigInteger result = bigCollection.parallelStream()
                     .map(e -> BigInteger.valueOf(someVeryComplexCalculation(e, ...)))
                     .reduce(BigInteger.ZERO, BigInteger::add);

您的问题是经典的 map-reduce 任务,您应该转换某个集合的每个元素,然后将各个转换的结果组合成最终结果。 Stream API 能够非常有效地并行处理此类任务,而无需任何手动工作。在 Oracle JDK 中,任务在 common ForkJoinPool pool 中执行默认情况下,它会创建与您拥有的 CPU 内核一样多的线程。

关于java - 同时求和的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32134456/

相关文章:

java - 实践中的约束布局看起来与 Android Studio 中不同

Java 同步

c++ - 无锁容器如何对并发分区和排序使用react?

java - 如何理解AQS上的 "unparkSuccessor"函数

java - 如何使用 wireshark 解密服务以服务 SSL 流量?

java - 从构造函数调用重写的方法

multithreading - Flutter Dart : How can we achieve multithreading like python or java in dart

opengl - HOpenGL关于Haskell中的其他线程和TChans的行为如何?

java - 如何声明扩展参数化类型的类型变量?

java - Java 中对象实例的上限