Java-多线程一个大循环

Java-多线程一个大循环,java,multithreading,loops,multiprocessing,threadpool,Java,Multithreading,Loops,Multiprocessing,Threadpool,这可能是一个相当简单的问题,但因为我以前从未使用过线程,所以我认为最好是提问,而不是完全靠我自己去寻找最佳解决方案 我有一个巨大的for循环,可以运行几十亿次。在每次循环运行时,根据当前的索引,程序以数字的形式计算最终结果。我只对存储top结果(或top x结果)及其相应的索引感兴趣 我的问题很简单,在线程中运行这个循环的正确方法是什么,以便它使用所有可用的CPU/内核 int topResultIndex; double topResult = 0; for (i=1; i < 100

这可能是一个相当简单的问题,但因为我以前从未使用过线程,所以我认为最好是提问,而不是完全靠我自己去寻找最佳解决方案

我有一个巨大的
for
循环,可以运行几十亿次。在每次循环运行时,根据当前的
索引
,程序以数字的形式计算最终结果。我只对存储top
结果(或top x结果)及其相应的索引感兴趣

我的问题很简单,在线程中运行这个循环的正确方法是什么,以便它使用所有可用的CPU/内核

int topResultIndex;
double topResult = 0;

for (i=1; i < 1000000000; ++i) {
    double result = // some complicated calculation based on the current index
    if (result > topResult) {
        topResult = result;
        topResultIndex = i;
    }
}
int-topResultIndex;
双topResult=0;
对于(i=1;i<100000000;++i){
double result=//基于当前索引的一些复杂计算
如果(结果>topResult){
topResult=结果;
topResultIndex=i;
}
}

每个索引的计算是完全独立的,没有共享资源<代码>topResultIndex
topResult
显然会被每个线程访问


*更新:Giulio和rolfl的解决方案都很好,也非常相似。只能接受其中一个作为我的答案。

最简单的方法是使用,并以
可运行
可调用
的方式提交任务。您可以使用Executors.newFixedThreadPool(Runtime.getRuntime().AvailableProcessor())创建一个
ExecutorService
,该服务将使用与处理器相同数量的线程。

除了观察到使用OpenMP或其他并行计算扩展的C程序将是一个更好的主意之外,Java的方法是创建一个“未来”任务来计算问题的子集:

private static final class Result {
   final int index;
   final double result;
   public Result (int index, double result) {
       this.result = result;
       this.index = index;
   }
}

// Calculate 10,000 values in each thead
int steps = 10000;
int cpucount = Runtime.getRuntime().availableProcessors();
ExecutorService service = Executors.newFixedThreadPool(cpucount);
ArrayList<Future<Result>> results = new ArrayList<>();
for (int i = 0; i < 1000000000; i+= steps) {
    final int from = i;
    final int to = from + steps;
    results.add(service.submit(new Callable<Result>() {
        public Result call() {
              int topResultIndex = -1;
              double topResult = 0;
              for (int j = from; j < to; j++) {
                  // do complicated things with 'j'
                      double result = // some complicated calculation based on the current index
                      if (result > topResult) {
                          topResult = result;
                          topResultIndex = j;
                      }
              }
              return new Result(topResultIndex, topResult);
        }
    });
 }

 service.shutdown();
 while (!service.isTerminated()) {
     System.out.println("Waiting for threads to complete");
     service.awaitTermination(10, TimeUnit.SECONDS);
 }
 Result best = null;
 for (Future<Result> fut : results) {
    if (best == null || fut.result > best.result) {
       best = fut;
    }
 }

 System.out.printf("Best result is %f at index %d\n", best.result, best.index);

Future<Result>
私有静态最终类结果{
最终整数指数;
最终双倍成绩;
公开结果(整数索引,双结果){
this.result=结果;
这个指数=指数;
}
}
//计算每个THAD中的10000个值
int步数=10000;
int cpucount=Runtime.getRuntime().availableProcessors();
ExecutorService=Executors.newFixedThreadPool(cpucount);
ArrayList结果=新建ArrayList();
对于(int i=0;i<100000000;i+=步数){
最终int from=i;
最终整数到=从+步数;
results.add(service.submit(new Callable()){
公共结果调用(){
int topResultIndex=-1;
双topResult=0;
for(int j=from;jtopResult){
topResult=结果;
topResultIndex=j;
}
}
返回新结果(topResultIndex、topResult);
}
});
}
service.shutdown();
而(!service.isTerminated()){
System.out.println(“等待线程完成”);
服务。等待终止(10,时间单位。秒);
}
结果最佳=空;
用于(未来未来未来:结果){
if(best==null | | fut.result>best.result){
最佳=未来;
}
}
System.out.printf(“最佳结果是索引%d处的%f”,Best.result,Best.index);
未来

让我们假设结果是由
calculateResult(long)
方法计算的,该方法是私有的和静态的,并且不访问任何静态字段(它也可以是非静态的,但仍然必须是线程安全的和并发可执行的,希望是线程受限的)

然后,我想这会做一些肮脏的工作:

public static class Response {
    int index;
    double result;
}

private static class MyTask implements Callable<Response> {
    private long from;
    private long to;

    public MyTask(long fromIndexInclusive, long toIndexExclusive) {
        this.from = fromIndexInclusive;
        this.to = toIndexExclusive;
    }

    public Response call() {
        int topResultIndex;
        double topResult = 0;

        for (long i = from; i < to; ++i) {
            double result = calculateResult(i);
            if (result > topResult) {
                topResult = result;
                topResultIndex = i;
            }
        }

        Response res = new Response();
        res.index = topResultIndex;
        res.result = topResult;
        return res;
    }
};

private static calculateResult(long index) { ... }

public Response interfaceMethod() {
    //You might want to make this static/shared/global
    ExecutorService svc = Executors.newCachedThreadPool();

    int chunks = Runtime.getRuntime().availableProcessors();
    long iterations = 1000000000;
    MyTask[] tasks = new MyTask[chunks];
    for (int i = 0; i < chunks; ++i) {
        //You'd better cast to double and round here
        tasks[i] = new MyTask(iterations / chunks * i, iterations / chunks * (i + 1));
    }

    List<Future<Response>> resp = svc.invokeAll(Arrays.asList(tasks));
    Iterator<Future<Response>> respIt = resp.iterator();

    //You'll have to handle exceptions here
    Response bestResponse = respIt.next().get();

    while (respIt.hasNext()) {
        Response r = respIt.next().get();
        if (r.result > bestResponse.result) {
            bestResponse = r;
        }
    }

    return bestResponse;
}
公共静态类响应{
整数指数;
双重结果;
}
私有静态类MyTask实现可调用{
私人长从;
私人长到;
公共MyTask(长从IndexInclusive,长到IndexExclusive){
this.from=fromIndexInclusive;
this.to=toIndexExclusive;
}
公众回应电话(){
int topResultIndex;
双topResult=0;
for(长i=from;itopResult){
topResult=结果;
topResultIndex=i;
}
}
Response res=新响应();
res.index=topResultIndex;
res.result=topResult;
返回res;
}
};
私有静态计算器结果(长索引){…}
公众响应接口方法(){
//您可能希望将此设置为静态/共享/全局
ExecutorService svc=Executors.newCachedThreadPool();
int chunks=Runtime.getRuntime().availableProcessor();
长迭代次数=100000000次;
MyTask[]任务=新的MyTask[块];
for(int i=0;ibestResponse.result){
最佳反应=r;
}
}
返回最佳响应;
}

根据我的经验,这种分块比为每个索引分配任务要快得多(特别是如果每个索引的计算负载很小,很可能是这样的话。所谓小,我的意思是不到半秒)。不过,编码有点困难,因为需要进行两步最大化(首先是块级,然后是全局级)。这样,如果计算完全基于cpu(不会过多地推送ram),则应获得几乎等于物理核数80%的加速比。

每个索引的计算是独立的还是将有用于计算的共享资源?计算是完全独立的