java – 同时求和的最佳方法

前端之家收集整理的这篇文章主要介绍了java – 同时求和的最佳方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试计算一些大数字.为了加快计算速度,我想利用多线程.每个线程都应该计算一个数字,最后计算一个总和.

我曾经看过一些适用于SumThread和Collector的东西,如下所示:

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轻松地超越这个,而不是像以下那样制作新的Threads:

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的事情,但我需要一些BigIntegers的加法器……

我的问题是:同时计算金额的最佳方法是什么?它是上述之一还是有完全不同(但更好)的方式?

解决方法

正如您所提到的,在Java-8中添加并使用有效最终变量的LongAdder,我假设您使用的是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相关文章