0

简而言之:我正在尝试使用 ExecutorService 来运行多个线程,并且多线程的计算时间比单线程的计算时间差得多,所以我试图找出我哪里出错了。如果细节不重要,请跳过下面的冗长部分以获取代码。

长篇大论:我设置了一个蒙特卡罗方法来计算半径为 1 的圆的面积来近似 PI 的值。我将其建模为使用随机的 x 和 y 坐标模拟在圆圈上投掷飞镖。经过大量的投掷,圆的面积可以近似为有多少飞镖击中它。

我创建了一个实现 runnable 的 DartTosser 类,以抛出需要抛出的飞镖总数的增量。我创建了一个带有实例字段的对象和一个同步方法来聚合来自多个 DartTossers 的 dart 命中。投完所有飞镖后,将估计并返回 pi。

从用户输入中读取线程数和投掷次数。然后按下按钮触发以下方法:

private double estimatePi(int numThreads, long numTosses, TossBin bin){
    long tossIncrement = numTosses/numThreads;
    ExecutorService executor = Executors.newFixedThreadPool(numThreads);

    for (int i = 0; i < numThreads; i++){
        executor.submit(new DartTosser(i, tossIncrement, bin));
    }

    executor.shutdown();

    System.out.println("All dart tossers have begun tossing darts.");

    try {
        executor.awaitTermination(1, TimeUnit.DAYS);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("All darts have been tossed.");

    return (4*bin.getTotalInCircle())/((double)numTosses);
}

DartTosser 类在其 run() 方法中触发以下方法,然后将击中圆圈的总投掷次数添加到 TossBin 对象的实例中:

private long startTossing(){

    for (int i = 0; i < localTosses; i++){
        // get two random doubles between -1 and 1
        x = generateRandomNumber();
        y = generateRandomNumber();
        distanceSquared = (x*x) + (y*y);
        if (distanceSquared <= 1.0){
            tossesInCircle++;
        }
    }

    return tossesInCircle;
}

为了完整起见,这就是我的 TossBin 类的样子:

public class TossBin {
    private long totalTossesInCircle = 0;

    public TossBin(){

    }

    public synchronized void addToTotalTosses(long n){
        this.totalTossesInCircle += n;
    }

    public void resetTossValues(){
        this.totalTossesInCircle = 0;
    }

    public long getTotalInCircle(){
        return totalTossesInCircle;
    }
}

PI 近似值很接近,所以我认为我的实际计算很好。运行单个线程,它将在大约 1600 毫秒左右处理 100 万次抛掷。运行两个线程,它将在大约 5600 毫秒左右处理 100 万次抛掷。线程越多,效率越低。我可以从系统输出中看到线程(似乎是)并发运行,但我的并行化显然是各种各样的。

我在 Galaxy S3 以及 eclipse 的模拟器上运行它,我看到了同样的总体趋势。有什么想法吗?

只是为了确保我已经晾干了所有可能脏的床单:

public class DartTosser implements Runnable {

private int id;
private long localTosses = 0;
private long tossesInCircle = 0;
double x, y, distanceSquared;
TossBin globalBin;

private double generateRandomNumber(){
    return (Math.random()*2.0)-1.0;

}

public DartTosser(int id, long tosses, TossBin globalBin){
    this.id = id;
    this.localTosses = tosses;
    this.globalBin = globalBin;
}

private long startTossing(){

    for (int i = 0; i < localTosses; i++){
        // get two random doubles between -1 and 1
        x = generateRandomNumber();
        y = generateRandomNumber();
        distanceSquared = (x*x) + (y*y);
        if (distanceSquared <= 1.0){
            tossesInCircle++;
        }
    }

    return tossesInCircle;
}

@Override
public void run() {
    System.out.println("Dart tosser number " + Integer.toString(id) + "starting work.");

    globalBin.addToTotalTosses(startTossing());

    System.out.println("Dart tosser number " + Integer.toString(id) + "finished work.");
}

}
4

1 回答 1

2

好的,这就是我认为正在发生的事情:

Math.random()使用 的单个实例Random,调用nextDouble()它本身是线程安全的,使用compare-and-swap

结果,线程在生成随机数时会花费大量时间互相等待。尝试为每个实例提供DartTosser其自己的Random实例并Random.nextDouble()改为使用。

于 2013-08-14T03:52:04.233 回答