2011-06-07 40 views
5

我正在尝试计算Pi,但是当使用多个线程时,我真正想实现的是效率。该算法很简单:我在单位正方形中随机生成点,然后计算它们中有多少个正方形内的圆。 (更多这里:http://math.fullerton.edu/mathews/n2003/montecarlopimod.html) 我的想法是水平拆分方块,并为它的每个部分运行不同的线程。但是不是加快速度,我得到的只是一个延迟。任何想法为什么?下面是代码:如何加快使用多线程的计算?

public class TaskManager { 

public static void main(String[] args) { 

    int threadsCount = 3; 
    int size = 10000000; 
    boolean isQuiet = false; 

    PiCalculator pi = new PiCalculator(size); 
    Thread tr[] = new Thread[threadsCount]; 

    long time = -System.currentTimeMillis(); 

    int i; 
    double s = 1.0/threadsCount; 
    int p = size/threadsCount; 

    for(i = 0; i < threadsCount; i++) { 
     PiRunnable r = new PiRunnable(pi, s*i, s*(1.0+i), p, isQuiet); 
     tr[i] = new Thread(r); 
    } 

    for(i = 0; i < threadsCount; i++) { 
     tr[i].start(); 
    } 

    for(i = 0; i < threadsCount; i++) { 
     try { 
      tr[i].join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    }  

    double myPi = 4.0*pi.getPointsInCircle()/pi.getPointsInSquare(); 
    System.out.println(myPi + " time = " + (System.currentTimeMillis()+time)); 
} 
} 

public class PiRunnable implements Runnable { 

PiCalculator pi; 
private double minX; 
private double maxX; 
private int pointsToSpread; 

public PiRunnable(PiCalculator pi, double minX, double maxX, int pointsToSpread, boolean isQuiet) { 
    super(); 
    this.pi = pi; 
    this.minX = minX; 
    this.maxX = maxX; 
    this.pointsToSpread = pointsToSpread; 
} 


@Override 
public void run() { 
    int n = countPointsInAreaInCircle(minX, maxX, pointsToSpread); 
    pi.addToPointsInCircle(n); 
} 

public int countPointsInAreaInCircle (double minX, double maxX, int pointsCount) { 
    double x; 
    double y; 

    int inCircle = 0; 

    for (int i = 0; i < pointsCount; i++) { 
     x = Math.random() * (maxX - minX) + minX; 
     y = Math.random();   

     if (x*x + y*y <= 1) { 
      inCircle++; 
     } 
    } 

    return inCircle; 

} 

} 


public class PiCalculator { 

private int pointsInSquare; 
private int pointsInCircle; 

public PiCalculator(int pointsInSquare) { 
    super(); 
    this.pointsInSquare = pointsInSquare; 
} 

public synchronized void addToPointsInCircle (int pointsCount) { 
    this.pointsInCircle += pointsCount; 
} 

public synchronized int getPointsInCircle() { 
    return this.pointsInCircle; 
} 

public synchronized void setPointsInSquare (int pointsInSquare) { 
    this.pointsInSquare = pointsInSquare; 
} 

public synchronized int getPointsInSquare() { 
    return this.pointsInSquare; 
} 

} 

的一些结果: - 对于3个线程: “3.1424696时间= 2803” - 用于1线: “3.1416192时间= 2337”

+2

您是否在多核系统上运行? – ribram 2011-06-07 19:35:59

+0

我在intel core 2上运行。 – 2011-06-07 19:40:45

+1

他在加入之前就开始了他们,所以这很好。如果你只有2个内核,那么使用2个以上的线程是没有用的。由于上下文切换的开销,您的应用程序纯粹受CPU限制,因此比内核多的线程会减慢速度。 – ribram 2011-06-07 19:54:42

回答

9

你的线程可以战斗/等待对于同步的Math.random(),应该为每个线程创建一个java.util.Random实例。同样在这种情况下,只有当您有多个核心/ CPU时,才会发生多线程加速。

Math.random()的Javadoc:

这种方法被适当地同步 ,以允许由一个以上的线程 正确使用。然而,如果许多线程需要 来以高速率生成伪随机数,则它可以减少每个线程拥有其自己的 伪随机数生成器的争用 。

+0

中使用了以下内容,非常感谢。它的工作原理:) – 2011-06-07 19:44:19

+1

我将'Math.random()'改为'Random'的本地实例,并得到以下输出:'3.1411296 time = 253' Core i3 M350 2.27GHZ/8GB RAM – 2011-06-07 19:56:11

1

这是另一种main方法,它使用java.util.concurrency包而不是手动管理线程并等待它们完成。

public static void main(final String[] args) throws InterruptedException 
{ 

    final int threadsCount = Runtime.getRuntime().availableProcessors(); 
    final int size = 10000000; 
    boolean isQuiet = false; 

    final PiCalculator pi = new PiCalculator(size); 
    final ExecutorService es = Executors.newFixedThreadPool(threadsCount); 

    long time = -System.currentTimeMillis(); 

    int i; 
    double s = 1.0/threadsCount; 
    int p = size/threadsCount; 

    for (i = 0; i < threadsCount; i++) 
    { 
     es.submit(new PiRunnable(pi, s * i, s * (1.0 + i), p, isQuiet)); 
    } 

    es.shutdown(); 

    while (!es.isTerminated()) { /* do nothing waiting for threads to complete */ } 

    double myPi = 4.0 * pi.getPointsInCircle()/pi.getPointsInSquare(); 
    System.out.println(myPi + " time = " + (System.currentTimeMillis() + time)); 
} 

我也改变了Math.random()使用的Random本地实例每个Runnable

final private Random rnd; 

...  

x = this.rnd.nextDouble() * (maxX - minX) + minX; 
y = this.rnd.nextDouble(); 

这是新的输出我得到...

3.1419284 time = 235 

我想你也许可能下降的时间更多一些使用Futures而不必同步在PiCalculator这么多。