使循环多线程,Java变得昂贵

时间:2018-06-21 02:22:54

标签: java multithreading concurrency future executorservice

我有一个要使用Java的ExecutorServiceFuture类解决的问题。我目前正在使用for循环从一个函数进行计算(每个样本可能要花费几分钟)的函数中获取大量样本(每个样本可能要花费几分钟)。我有一个FunctionEvaluator类,可以为我评估该函数,并且由于包含大量内部内存,因此该类的实例化非常昂贵,因此我使该类易于使用一些内部计数器和reset()进行重用。方法。所以我目前的情况是这样的:

int numSamples = 100;
int amountOfData = 1000000;
double[] data = new double[amountOfData];//Data comes from somewhere...
double[] results = new double[numSamples];
//a lot of memory contained inside the FunctionEvaluator class,
//expensive to intialise
FunctionEvaluator fe = new FunctionEvaluator();

for(int i=0; i<numSamples; i++) {
    results[i] = fe.sampleAt(i, data);//very expensive computation
}

但是我想获得一些多线程来加快处理速度。这应该足够容易,因为尽管每个样本将共享data内部的任何内容,但这是一个只读操作,并且每个样本彼此独立。现在,因为我之前使用过Java的FutureExecutorService,所以我对此不会有任何麻烦,但是从来没有在必须重复使用Callable的情况下使用。因此,总的来说,如果我有能力运行n的{​​{1}}实例化,我将如何设置这种情况?大概是这样的:

FunctionEvaluator

任何帮助将不胜感激!非常感谢。

编辑

因此,这是一个玩具示例(不起作用:P)。在这种情况下,我要采样的“昂贵函数”只是对整数进行平方,而对我有用的“昂贵实例化类”称为int numSamples = 100; int amountOfData = 1000000; int N = 10; double[] data = new double[amountOfData];//Data comes from somewhere... double[] results = new double[numSamples]; //a lot of memory contained inside the FunctionEvaluator class, //expensive to intialise FunctionEvaluator[] fe = new FunctionEvaluator[N]; for(int i=0; i<numSamples; i++) { //Somehow add available FunctionEvaluators to an ExecutorService //so that N FunctionEvaluators can run in parallel. When a //FunctionEvaluator is finished, reset then compute a new sample //until numSamples samples have been taken. }

在TestConc.java中:

CallableComputation

在CallableComputation.java中:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class TestConc {

    public static void main(String[] args) {
        SquareCalculator squareCalculator = new SquareCalculator();
        int numFunctionEvaluators = 2;
        int numSamples = 10;

        ExecutorService executor = Executors.newFixedThreadPool(2);
        CallableComputation c1 = new CallableComputation(2);
        CallableComputation c2 = new CallableComputation(3);

        CallableComputation[] callables = new CallableComputation[numFunctionEvaluators];
        Future<Integer>[] futures = (new Future[numFunctionEvaluators]);
        int[] results = new int[numSamples];

        for(int i=0; i<numFunctionEvaluators; i++) {
            callables[i] = new CallableComputation(i);
            futures[i] = executor.submit(callables[i]);
        }

        futures[0] = executor.submit(c1);
        futures[1] = executor.submit(c2);

        for(int i=numFunctionEvaluators; i<numSamples; ) {
            for(int j=0; j<futures.length; j++) {
                if(futures[j].isDone()) {
                    try {
                        results[i] = futures[j].get();
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    catch (ExecutionException e) {
                        e.printStackTrace();
                    }
                    callables[j].set(i);
                    System.out.printf("Function evaluator %d given %d\n", j, i+1);
                    executor.submit(callables[j]);
                    i++;
                }
            }
        }
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.MINUTES);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i=0; i<results.length; i++) {
            System.out.printf("res%d=%d, ", i, results[i]);
        }
        System.out.println();
    }

    private static boolean areDone(Future<Integer>[] futures) {
        for(int i=0; i<futures.length; i++) {
            if(!futures[i].isDone()) {
                return false;
            }
        }
        return true;
    }

    private static void printFutures(Future<Integer>[] futures) {
        for (int i=0; i<futures.length; i++) {
            System.out.printf("f%d=%s | ", i, futures[i].isDone()?"done" : "not done");
        }System.out.printf("\n");
    }

}

4 个答案:

答案 0 :(得分:2)

在Java8中:

double[] result = IntStream.range(0, numSamples)
    .parallel()
    .mapToDouble(i->fe.sampleAt(i, data))
    .toArray();

问题询问如何通过加载尽可能多的CPU来并行执行繁重的计算功能。

Parallelism tutorial执行:

  

并行计算涉及将问题分解为子问题,   同时解决这些问题(并行解决每个问题   子问题在单独的线程中运行),然后将   子问题解决方案的结果。 Java SE提供了   fork / join框架,使您可以更轻松地实现   应用程序中的并行计算。但是,有了这个框架,   您必须指定问题的细分方式。用   聚合操作后,Java运行时将执行此分区,并且   为您组合解决方案。

实际的解决方案包括:

  • IntStream.range将生成从0到numSamples的整数流。

  • parallel()将拆分流并执行该流,将使盒子上的所有可用CPU都运行。

  • mapToDouble()将通过应用将执行实际工作的lamba表达式将整数流转换为双精度流。

  • toArray()是一种终端操作,它将汇总结果并将其作为数组返回。

答案 1 :(得分:0)

不需要特殊代码更改,您可以一次又一次使用相同的Callable,而不会出现任何问题。同样,要提高效率,正如您所说的那样,创建FunctionEvaluator的实例非常昂贵,您只能使用一个实例并确保sampleAt是线程安全的。一种选择是,也许您可​​以使用所有函数局部变量,并且在任何线程运行时的任何时间都不要修改任何传递的参数

请在下面找到一个简单的示例:

代码段:

ExecutorService executor = Executors.newFixedThreadPool(2);
Callable<String> task1 = new Callable<String>(){public String call(){System.out.println(Thread.currentThread()+"currentThread");return null;}}
executor.submit(task1);
executor.submit(task1);
executor.shutdown();

请在下面找到屏幕截图:

enter image description here

答案 2 :(得分:0)

您可以将每个FunctionEvaluator的实际工作包装为Callable / Runnanle,然后使用带有队列的fixdThreadPool,然后只需将目标可调用/可运行对象汇总到threadPool。

答案 3 :(得分:0)

  

我想获得一些多线程来加快处理速度。

听起来不错,但是您的代码过于复杂。 @Pavel有一个简单的Java 8解决方案,但是即使没有Java 8,您也可以使它变得更加简单。

您需要做的就是将作业提交给执行程序,然后在返回的get()的每一个上调用Future。不需要Callable类,尽管它确实使代码更简洁。但是您肯定不需要阵列,这是一种不好的模式,因为输入错误很容易产生越界异常。坚持收集或Java 8流。

ExecutorService executor = Executors.newFixedThreadPool(2);
List<Future<Integer>> futureList = new ArrayList<Future<Integer>>();
for (int i = 0; i < numSamples; i++ ) {
    // start the jobs running in the background
    futureList.add(executor.subject(new CallableComputation(i));
}
// shutdown executor if done submitting tasks, submitted jobs will keep running
executor.shutdown();
for (Future<Integer> future : futureList) {
    // this will wait for the future to finish, it also throws some exceptions
    Integer result = future.get();
    // add result to a collection or something here
}