缺点:我正在尝试使用ExecutorService来运行多个线程,并且多个线程的计算时间太多更糟,而不是单个线程,所以我想弄清楚我在哪里出错。如果细节不重要,请跳过以下内容以获取代码。
很长一段时间:我设置了一个monte carlo方法来计算半径为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.");
}
}
答案 0 :(得分:2)
好的,这就是我的想法:
Math.random()
使用Random
的单个实例,使用nextDouble()
调用本身是线程安全的compare-and-swap。
因此,线程在生成随机数时会花费大量时间等待彼此。尝试为每个DartTosser
实例提供自己的Random
实例,并改为使用Random.nextDouble()
。