我在制作多线程应用程序方面没有太多经验,但我觉得我的程序可能会因为拥有多个线程而受益。我正在做一个更大规模的项目,涉及使用分类器(如机器学习)来分类大约32000个客户。我调试了程序,发现每个用户需要大约一秒钟的时间。换句话说,这需要8.8小时才能完成!
有什么方法可以运行4个线程,每个线程处理8000个用户?第一个线程将处理1-8000,第二个线程为8001-16000,第三个线程为16001-23000,第四个为23001-32000。此外,截至目前,每个分类都是通过从另一个类调用静态函数来完成的......
然后当主要的其他线程结束时。这样的事情可行吗?如果是这样,如果有人可以提供有关如何执行此操作的提示或步骤,我将非常感激。我熟悉关键部分(等待/信号)的想法,但对它没什么经验。
再次,非常感谢任何帮助!欢迎提出如何处理这种情况的提示和建议!不确定这是否重要,但我有一台处理器速度为2.53 GHZ的Core 2 Duo PC。
答案 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;
}
}
}