我正在尝试计算一些大数字.为了加快计算速度,我想利用多线程.每个线程都应该计算一个数字,最后计算一个总和.
我曾经看过一些适用于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(); } }@H_301_5@我以为我可以通过使用
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; }@H_301_5@但是,此代码无法胜过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);@H_301_5@您的问题是经典的map-reduce任务,您应该在其中转换某些集合的每个元素,然后将各个转换的结果合并到最终结果中. Stream API能够非常有效地并行化这些任务,而无需任何手动工作.在Oracle JDK中,任务在common ForkJoinPool pool中执行,默认情况下会创建与您拥有的cpu核心数一样多的线程.