2017-05-19 83 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