2017-05-19 72 views
0

我正在尝试创建一个简单的程序来计算PI,并教导自己如何使用多线程。每次运行该程序时,我都会得到不同的PI答案。我认为它与每次以不同顺序运行的线程有关,但是在输出我的答案之前,我在每个线程上都调用了Thread.join(),所以我不确定为什么计算的顺序是在会影响我的最终答案。我也尝试创建一个同步方法,为了使Pi变量线程的更新安全,并将volatile修饰符添加到我的pi变量中,我已命名为add,但是这并未阻止问题的发生。我的代码保持在一个单一类,即多线程Pi计算器不一致的答案

public class Pi implements Runnable{ 
    static volatile double pi = 0; 
    static long numRects = 100; 
    static int rectsPerThread = 1 ; 
    static double width = 1.0/numRects; 
    long start, end; 

    public Pi(long start, long end){ 
     this.start = start; 
     this.end = end; 
    } 

    @Override 
    public void run(){ 
     double tmp = 0; 
     for(long i = start; i < end; i++){ 
      tmp += Math.sqrt(1.0 - Math.pow(width * i, 2)) * width; 
     } 
     add(tmp); 
    } 

    private synchronized void add(double partOfPi){ 
     pi += partOfPi; 
    } 

    public static void main(String[] args){ 
     Thread[] threads = new Thread[(int)(numRects/rectsPerThread)]; 
     double start = System.nanoTime(); 
     for(int i = 0; i < threads.length; i++){ 
      threads[i] = new Thread(new Pi(i * rectsPerThread, i * rectsPerThread + rectsPerThread)); 
      threads[i].start(); 
     } 

     for(Thread t : threads){ 
      try{ 
       t.join(); 
      }catch(InterruptedException e){ 
       e.printStackTrace(); 
      } 
     } 

     pi *= 4; 
     System.out.println(pi); 
     System.out.printf("Ran in: %.4fms", (System.nanoTime() - start)/Math.pow(10, 6)); 
    } 
} 

我的问题是:我在做什么毛病我的多线程会导致不同的答案每次都被退回,哪有我去解决这个问题?

+0

是读写'pi'线程安全的值吗? –

+0

**为什么**你在做这个? Math.PI存在。 –

+0

@ScaryWombat可能会解释什么是问题,但是即使添加一个同步方法添加到Pi出现同样的问题 - http://prntscr.com/f9ifq7 – quixotrykd

回答

0

下面,

var += expr; 

不是一个原子操作。旧值被读取,递增并写回,所以它不是线程安全的。你需要用信号来防范它。

import java.util.concurrent.Semaphore; 
public class Pi implements Runnable{ 
    static double pi = 0; 
    static long numRects = 100; 
    static int rectsPerThread = 1; 
    static double width = 1.0/numRects; 
    long start, end; 
    private static final Semaphore s = new Semaphore(1); 

    public Pi(long start, long end){ 
     this.start = start; 
     this.end = end; 
    } 

    @Override 
    public void run(){ 
     for(long i = start; i < end; i++){ 
      try{ 
       s.acquire(); 
      }catch (InterruptedException IE){ 
       return; 
      } 
      pi += Math.sqrt(1.0 - Math.pow(width * i, 2)) * width; 
      s.release(); 
     } 
    } 

    public static void main(String[] args){ 
     Thread[] threads = new Thread[(int)(numRects/rectsPerThread)]; 
     double start = System.nanoTime(); 
     for(int i = 0; i < threads.length; i++){ 
      threads[i] = new Thread(new Pi(i * rectsPerThread, i * rectsPerThread + rectsPerThread)); 
      threads[i].start(); 
     } 

     for(Thread t : threads){ 
      try{ 
       t.join(); 
      }catch(InterruptedException e){ 
       e.printStackTrace(); 
      } 
     } 

     pi *= 4; 
     System.out.println(pi); 
     System.out.printf("Ran in: %.4fms", (System.nanoTime() - start)/Math.pow(10, 6)); 
    } 
} 
+0

这是有道理的。我已经使用额外的代码更新了原始问题,使用外部同步添加方法来使添加线程安全。为什么使用信号量工作,而使用同步添加方法不起作用? – quixotrykd

+1

同步方法在同一个对象中工作;即它保证了在同一个Pi实例内的互斥。您每次创建一个新的Pi。 – user3109672

+0

我已将我的Pi变量设为静态。除非我对静态变量的理解很重要,这意味着我只有一个pi实例。 – quixotrykd