4个线程中的多线程自动执行相同操作



我写了一个程序来扫描友好数字(一对2个数字,其中一个的所有偏差之和等于另一个)。它工作正常,我将包括下面的整个代码。我试图让它用几个线程运行,所以我把代码移到了一个名为Breaker的类中,我的主要外观如下:

    Breaker line1 = new Breaker("thread1");
    Breaker line2 = new Breaker("thread2");
    Breaker line3 = new Breaker("thread3");
    Breaker line4 = new Breaker("thread4");
    line1.scanRange(1L, 650000L);
    line2.scanRange(650001L, 850000L);
    line3.scanRange(850001L, 1000000L);
    line4.scanRange(1000001L, 1200001L);

现在,这确实明显缩短了时间,但这不是一个明智的解决方案,线程在非常不同的时间结束。

我想做的是自动化这个过程,这样一个拥有整个范围的主线程就会从主范围中激发出短范围(10000)的部分,当一个线程结束时,激发新线程中的下一个部分,直到整个主范围结束。

我试着理解如何使用synchronized、notify()和wait(),但经过几次尝试后,都以不同的错误和不必要的行为告终。

这是Breaker.java:

import java.util.ArrayList;
public class Breaker implements Runnable{
Long from, to = null;
String name = null;
Thread t = new Thread(this);
public Breaker(String name){
    this.name = name;
}
public void scanRange(Long from, Long to){
    this.from = from;
    this.to = to;
    t.start();
}
@Override
public void run() {
    this.scan();
}
private void scan() {
    ArrayList<ArrayList<Long>> results = new ArrayList<ArrayList<Long>>();
    Long startingTime = new Long(System.currentTimeMillis() / 1000L);
    Long lastReport = new Long(startingTime);
    System.out.println(startingTime + ": Starting number is: " + this.from);
    for (Long i = this.from; i <= this.to; i++) {
        if (((System.currentTimeMillis() / 1000L) - startingTime ) % 60 == 0 && (System.currentTimeMillis() / 1000L) != lastReport) {
            System.out.println((System.currentTimeMillis() / 1000L) + ": " + this.name + " DOING NOW " + i.toString() + ".");
            lastReport = (System.currentTimeMillis() / 1000L);
        }
        ArrayList<Long> a = new ArrayList<Long>();
        a = getFriendPair(i);
        if(a != null) {
            results.add(a);
            System.out.println(this.name + ": FOUND PAIR! " + a.toString());
        }
    }
    System.out.println((System.currentTimeMillis() / 1000L) + ": " + this.name + " Done. Total pairs found: " + results.size() + 
            ". Total working time: " + ((System.currentTimeMillis() / 1000L) - startingTime) + " seconds.");

}
/**
 * Receives integer and returns an array of the integer and the number who is it's
 * pair in case it has any. Else returns null.
 * @param i
 * @return
 */
private static ArrayList<Long> getFriendPair(Long i) {
    Long possibleFriend = getAndSumAllDevisors(i);
    if (possibleFriend.compareTo(i) <= 0) return null;
    Long sumOfPossibleFriend = getAndSumAllDevisors(possibleFriend);
    if(sumOfPossibleFriend.equals(i)) {
        ArrayList<Long> pair = new ArrayList<Long>();
        pair.add(i);
        pair.add(possibleFriend);
        return pair;
    }
    return null;
}
private static Long getAndSumAllDevisors(Long victim) {
    Long sum = new Long(1);
    Long i = 2L;
    Long k = new Long(0);
    while ((k = i * i) <= victim) {
        if ((victim % i) == 0) {
            sum += i;
            if (k == victim) return sum;
            sum += (victim / i);
        }
        i++;
    }
    return sum;
}
}

考虑由线程池支持的ExecutorService。你给它提供任务,当它们可用时,它们会被转移到工作线程:

http://www.vogella.com/articles/JavaConcurrency/article.html#threadpools

您需要的是一个"固定线程池"。看见http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool%28int%29

我会选择具有固定线程池大小的ExecutorService。您的主线程可以直接馈送到执行器服务,也可以通过BlockingQueue断开它们的连接。阻塞队列的Java文档非常好地描述了生产者-消费者模式。

我最终没有接受任何答案,而是接受了Marko的评论,并使用Fork/Join框架实现了我的解决方案。它的工作和运行速度几乎是未优化版本的两倍。

我的代码现在看起来是这样的:

主文件(运行程序)

public class runner {
private static Long START_NUM = 1L;
private static Long END_NUM =   10000000L;
public static void main(String[] args) {
    Long preciseStartingTime = new Long(System.currentTimeMillis());
    ForkJoinPool pool = new ForkJoinPool();
    WorkManager worker = new WorkManager(START_NUM, END_NUM);
    pool.invoke(worker);
    System.out.println("precise time: " + (System.currentTimeMillis() - preciseStartingTime));
}

WorkManager

我在这里定义了3个类变量。CCD_ 1和CCD_。threshold是程序将分配给单个线程串行计算的最大数量。正如你在代码中看到的,它会递归地使范围变小,直到它足够小,可以直接计算,然后它调用Breaker开始中断。

import java.util.concurrent.RecursiveAction;
public class WorkManager extends RecursiveAction{
Long from, to;
Long threshold = 10000L;
public WorkManager(Long from, Long to) {
    this.from = from;
    this.to = to;
}
protected void computeDirectly(){
    Breaker b = new Breaker(from, to);
    b.scan();
}
@Override
protected void compute() {
    if ((to - from) <= threshold){
        System.out.println("New thread from " + from + " to " + to);
        computeDirectly();
    }
    else{
        Long split = (to - from) /2;
        invokeAll(new WorkManager(from, from + split),
                new WorkManager(from + split + 1L, to));
    }
}
}

断路器(不再是Runnable的实现)

public class Breaker{
Long from, to = null;
public Breaker(Long lFrom, Long lTo) {
    this.from = lFrom;
    this.to   = lTo;
}
public void scan() {
    ArrayList<ArrayList<Long>> results = new ArrayList<ArrayList<Long>>();
    Long startingTime = new Long(System.currentTimeMillis() / 1000L);
    for (Long i = this.from; i <= this.to; i++) {
        ArrayList<Long> a = new ArrayList<Long>();
        a = getFriendPair(i);
        if(a != null) {
            results.add(a);
            System.out.println((System.currentTimeMillis() / 1000L) + ": FOUND PAIR! " + a.toString());
        }
    }
}
/**
 * Receives integer and returns an array of the integer and the number who is it's
 * pair in case it has any. Else returns null.
 * @param i
 * @return
 */
private static ArrayList<Long> getFriendPair(Long i) {
    Long possibleFriend = getAndSumAllDevisors(i);
    if (possibleFriend.compareTo(i) <= 0) return null;
    Long sumOfPossibleFriend = getAndSumAllDevisors(possibleFriend);
    if(sumOfPossibleFriend.equals(i)) {
        ArrayList<Long> pair = new ArrayList<Long>();
        pair.add(i);
        pair.add(possibleFriend);
        return pair;
    }
    return null;
}
private static Long getAndSumAllDevisors(Long victim) {
    Long sum = new Long(1);
    Long i = 2L;
    Long k = new Long(0);
    while ((k = i * i) <= victim) {
        if ((victim % i) == 0) {
            sum += i;
            if (k == victim) return sum;
            sum += (victim / i);
        }
        i++;
    }
    return sum;
}
}