2008-10-29 66 views
2

我有一些線程相關的問題,假設下面的代碼。請忽略代碼可能的低效率,我只對線程部分感興趣。代碼性能比較,線程與非線程

//code without thread use 
public static int getNextPrime(int from) { 
    int nextPrime = from+1; 
    boolean superPrime = false; 
    while(!superPrime) { 
     boolean prime = true; 
     for(int i = 2;i < nextPrime;i++) { 
      if(nextPrime % i == 0) { 
       prime = false; 
      } 
     } 
     if(prime) { 
      superPrime = true; 
     } else { 
      nextPrime++; 
     } 
    } 
    return nextPrime; 
} 

public static void main(String[] args) { 
    int primeStart = 5; 
    ArrayList list = new ArrayList(); 
    for(int i = 0;i < 10000;i++) { 
     list.add(primeStart); 
     primeStart = getNextPrime(primeStart); 
    } 
} 

如果我正在運行這樣的代碼,它大約需要56秒。但是,如果我有以下代碼(作爲替代):

public class PrimeRunnable implements Runnable { 

    private int from; 
    private int lastPrime; 

    public PrimeRunnable(int from) { 
     this.from = from; 
    } 

    public boolean isPrime(int number) { 
     for(int i = 2;i < from;i++) { 
      if((number % i) == 0) { 
       return false; 
      } 
     } 
     lastPrime = number; 
     return true; 
    } 

    public int getLastPrime() { 
     return lastPrime; 
    } 

    public void run() { 
     while(!isPrime(++from)) 
      ; 
    } 
} 

public static void main(String[] args) { 
    int primeStart = 5; 
    ArrayList list = new ArrayList(); 
    for(int i = 0;i < 10000;i++) { 
    PrimeRunnable pr = new PrimeRunnable(primeStart); 
    Thread t = new Thread(pr); 
    t.start(); 
    t.join(); 
    primeStart = pr.getLastPrime(); 
    list.add(primeStart); 
    } 
} 

整個操作大約需要7秒。我幾乎可以肯定的是,即使我一次只創建一個線程,但創建另一個線程時線程並不總是完成。是對的嗎?我也很好奇:爲什麼這個行動結束得這麼快?

當我加入一個線程時,其他線程是否繼續在後臺運行,或者是連接的線程是唯一正在運行的線程?

回答

3

通過將join()方法的循環,你啓動一個線程,然後等待該線程在運行下一個之前停止。我想你也許想要更多的東西是這樣的:

public static void main(String[] args) { 
    int primeStart = 5; 

    // Make thread-safe list for adding results to 
    List list = Collections.synchronizedList(new ArrayList()); 

    // Pull thread pool count out into a value so you can easily change it 
    int threadCount = 10000; 
    Thread[] threads = new Thread[threadCount]; 

    // Start all threads 
    for(int i = 0;i < threadCount;i++) { 
    // Pass list to each Runnable here 
    // Also, I added +i here as I think the intention is 
    // to test 10000 possible numbers>5 for primeness - 
    // was testing 5 in all loops 
    PrimeRunnable pr = new PrimeRunnable(primeStart+i, list); 
    Thread[i] threads = new Thread(pr); 
    threads[i].start(); // thread is now running in parallel 
    } 

    // All threads now running in parallel 

    // Then wait for all threads to complete 
    for(int i=0; i<threadCount; i++) { 
    threads[i].join(); 
    } 
} 

順便說pr.getLastPrime()將在沒有總理的情況下返回0,所以你可能想將它添加到你的列表之前過濾了這一點。 PrimeRunnable必須吸收添加到最終結果列表中的工作。另外,我認爲PrimeRunnable實際上已被打破,仍然有遞增的代碼。我認爲這是固定的,但我實際上並沒有編譯這個。

public class PrimeRunnable implements Runnable {  
    private int from; 
    private List results; // shared but thread-safe 

    public PrimeRunnable(int from, List results) { 
     this.from = from; 
     this.results = results; 
    } 

    public void isPrime(int number) { 
     for(int i = 2;i < from;i++) { 
       if((number % i) == 0) { 
         return; 
       } 
     } 
     // found prime, add to shared results 
     this.results.add(number); 
    } 

    public void run() { 
     isPrime(from);  // don't increment, just check one number 
    }  
} 

並行運行10000個線程不是一個好主意。創建一個合理大小的固定線程池並讓他們從共享隊列中提取工作是一個好主意。基本上每個工作人員都從同一隊列中抽取任務,對其進行處理並將結果保存在某個地方。與Java 5 +的最接近的端口是使用由線程池支持的ExecutorService。您還可以使用將ExecutorService與結果隊列組合在一起的CompletionService。

一個ExecutorService版本會是什麼樣子:

public static void main(String[] args) { 
    int primeStart = 5; 

    // Make thread-safe list for adding results to 
    List list = Collections.synchronizedList(new ArrayList()); 

    int threadCount = 16; // Experiment with this to find best on your machine 
    ExecutorService exec = Executors.newFixedThreadPool(threadCount); 

    int workCount = 10000; // See how # of work is now separate from # of threads? 
    for(int i = 0;i < workCount;i++) { 
    // submit work to the svc for execution across the thread pool 
    exec.execute(new PrimeRunnable(primeStart+i, list)); 
    } 

    // Wait for all tasks to be done or timeout to go off 
    exec.awaitTermination(1, TimeUnit.DAYS); 
} 

。希望給你一些想法。我希望最後一個例子比第一個更好。

1

仔細閱讀您的代碼。這兩種情況並不是一回事,它與線程無關。

當你加入一個線程時,其他線程將在後臺運行,是的。

+0

您能否告訴我您的意思是「這兩起案件都沒有做同樣的事情」? – Geo 2008-10-29 21:20:39

+0

其中一種情況比較慢的原因是它沒有做同樣的事情。提示:您分別調用isPrime()和getNextPrime()多少次? – JesperE 2008-10-29 21:34:01

0

運行一個測試,第二個似乎不需要9秒鐘 - 事實上,它至少需要和第一個一樣長(這是可以預料的,threding無法幫助它實現的方式你的榜樣

的Thread.join只會返回時thread.joined終止,那麼當前線程將繼續,你叫加入的人會死

對於一個快速參考。 - 認爲穿線時開始一次迭代並不取決於前一次的結果

+0

我在我的筆記本電腦上試了兩次。我在帖子中寫的時間是我得到的時間。 – Geo 2008-10-29 21:19:29

+0

也許關於我們的虛擬機如何處理不同的東西。可能是因爲你的thread.join被破壞了,這將解釋運行時的差異......另外,第二個拋出了一個你沒有處理的異常。我不知道你是如何運行它的。你在用什麼虛擬機? – 2008-10-29 22:28:43

2

您可以通過在您的第一個例子運行線程。用你的主要方法:

private static int currentPrime; 
public static void main(String[] args) throws InterruptedException { 
    for (currentPrime = 0; currentPrime < 10000; currentPrime++) { 
     Thread t = new Thread(new Runnable() { 
      public void run() { 
       getNextPrime(currentPrime); 
      }}); 
     t.run(); 
     t.join(); 
    } 
} 

這將運行在原來的同一時間。

回答你的「加入」問題:是的,當你使用「加入」時,其他線程可以在後臺運行,但在這種情況下,一次只能有一個活動線程,因爲你阻止在最後一個線程完成執行之前創建新線程。

2

JesperE是正確的,但我並不只給提示(至少以外的教室)認爲:

注意這個循環中,非線程版本:

for(int i = 2;i < nextPrime;i++) { 
    if(nextPrime % i == 0) { 
    prime = false; 
    } 
} 

至於反對這線程版本:

for(int i = 2;i < from;i++) { 
    if((number % i) == 0) { 
    return false; 
    } 
} 

第一循環將總是完全貫穿,而如果它找到一個除數第二個會提前退出。

你可以做第一個循環也加入了break語句像這樣提前退出:

for(int i = 2;i < nextPrime;i++) { 
    if(nextPrime % i == 0) { 
    prime = false; 
    break; 
    } 
}