在Java中使用多个线程缩短程序时间

时间:2013-10-25 02:22:41

标签: java multithreading concurrency

我在制作多线程应用程序方面没有太多经验,但我觉得我的程序可能会因为拥有多个线程而受益。我正在做一个更大规模的项目,涉及使用分类器(如机器学习)来分类大约32000个客户。我调试了程序,发现每个用户需要大约一秒钟的时间。换句话说,这需要8.8小时才能完成!

有什么方法可以运行4个线程,每个线程处理8000个用户?第一个线程将处理1-8000,第二个线程为8001-16000,第三个线程为16001-23000,第四个为23001-32000。此外,截至目前,每个分类都是通过从另一个类调用静态函数来完成的......

然后当主要的其他线程结束时。这样的事情可行吗?如果是这样,如果有人可以提供有关如何执行此操作的提示或步骤,我将非常感激。我熟悉关键部分(等待/信号)的想法,但对它没什么经验。

再次,非常感谢任何帮助!欢迎提出如何处理这种情况的提示和建议!不确定这是否重要,但我有一台处理器速度为2.53 GHZ的Core 2 Duo PC。

4 个答案:

答案 0 :(得分:2)

这对于Apache Hadoop来说太轻了,每个服务器需要大约64MB的数据块...但是......这对Akka Actors来说是一个绝佳的机会,它恰好支持Java!

http://doc.akka.io/docs/akka/2.1.4/java/untyped-actors.html

基本上,你可以让4个演员完成这项工作,当他们完成对一个用户或可能更好的一些用户的分类时,他们要么将它传递给一个“接收者”演员,它将信息放入一个数据结构中或者用于输出的文件,或者,您可以通过每次写入文件来执行并发I / O ..然后可以在完成所有文件后检查/组合文件。

如果你想获得更多的幻想/强大,你可以将演员放在远程服务器上。与它们进行通信仍然非常容易,并且您将利用多个服务器的CPU /资源。

我自己写了一篇关于Akka演员的文章,但它是在Scala中,所以我会饶恕你。但是如果你谷歌“akka演员”,你会得到很多关于如何使用它的手持实例。勇敢,潜入并进行实验。 “演员系统”是一个容易接受的概念。我知道你可以做到!

答案 1 :(得分:1)

将数据拆分为实现Runnable的对象,然后将它们传递给新线程。

在这种情况下拥有四个以上的线程不会杀死你,但你不能获得比核心更多的并行工作(如评论中所述) - 如果线程多于核心,系统必须处理谁去的时候。

如果我有一个班级客户,并且我想发布一个线程来优先考虑更多收藏的8000个客户,我可能会这样做:

public class CustomerClassifier implements Runnable {

  private customer[] customers;

  public CustomerClassifier(customer[] customers) {
     this.customers = customers;
  }
  @Override
  public void run() {
    for (int i=0; i< customers.length; i++) {
      classify(customer);//critical that this classify function does not
                         //attempt to modify a resource outside this class
                         //unless it handles locking, or is talking to a database
                         //or something that won't throw fits about resource locking
    }
  }  
}

然后在其他地方发布这些线程

int jobSize = 8000;
customer[] customers = new customer[jobSize]();
int j = 0;
for (int i =0; i+j< fullCustomerArray.length; i++) {
  if (i == jobSize-1) {
    new Thread(new CustomerClassifier(customers)).start();//run will be invoked by thread
    customers = new Customer[jobSize]();
    j += i;
    i = 0;
  }
  customers[i] = fullCustomerArray[i+j];
}

如果您的分类方法影响了您必须使用的相同资源  实现锁定,也会在某种程度上扼杀你的优势。

并发非常复杂,需要经过深思熟虑,我还建议您查看oracle docs http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html (我知道链接很糟糕,但希望oracle文档不会过多地移动?)

免责声明:我不是并发设计或多线程(不同主题)的专家。

答案 2 :(得分:1)

如果将输入数组拆分为4个相等的子阵列中的4个线程,则无法保证所有线程同时完成。您最好将所有数据放在一个队列中,让所有工作线程从该公共队列中提取。使用安全的BlockingQueue实现,以便不写低级同步/等待/通知代码。

答案 3 :(得分:0)

从java 6开始,我们有一些方便的并发工具。您可能需要考虑使用线程池来实现更清晰的实现。

package com.threads;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ParalleliseArrayConsumption {

    private int[] itemsToBeProcessed ;

    public ParalleliseArrayConsumption(int size){
        itemsToBeProcessed = new int[size];
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        (new ParalleliseArrayConsumption(32)).processUsers(4);

    }

    public void processUsers(int numOfWorkerThreads){
         ExecutorService threadPool = Executors.newFixedThreadPool(numOfWorkerThreads);
         int chunk = itemsToBeProcessed.length/numOfWorkerThreads;
         int start = 0;
         List<Future> tasks = new ArrayList<Future>();
         for(int i=0;i<numOfWorkerThreads;i++){
             tasks.add(threadPool.submit(new WorkerThread(start, start+chunk)));
             start = start+chunk;
         }
             // join all worker threads to main thread
         for(Future f:tasks){
             try {
                f.get();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         }

        threadPool.shutdown();
        while(!threadPool.isTerminated()){
        }

    }

    private class WorkerThread implements Callable{

        private int startIndex;
        private int endIndex;

        public WorkerThread(int startIndex, int endIndex){
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public Object call() throws Exception {
            for(int currentUserIndex = startIndex;currentUserIndex<endIndex;currentUserIndex++){
                // process the user. Add your logic here
                System.out.println(currentUserIndex+" is the user being processed in thread " +Thread.currentThread().getName());
            }
            return null;
        }       

    }

}