問題原型:求三千萬之內的素數.java
問題的解決辦法很簡單,寫一個求素數算法 而後循環就能夠.這個是順序執行方式,下面附上代碼算法
public abstract class PrimeAbstract { public boolean isPrime(int i){ if(i<=1)return false; else{ for(int j=2; j<=Math.sqrt(i);j++){ if(i%j == 0)return false; } return true; } } public static void print(Object a){ System.out.println(a); } public int countPrimeInRange(int low, int up){ int total = 0; for(int i=low; i<=up; i++){ if(isPrime(i))total++; } return total; } public void timeAndCompute(final int number){ final long start =System.nanoTime(); final long num = countPrimes(number); final long end = System.nanoTime(); print("小於"+number+"的素數的個數爲"+num+"花費的時間"+(end-start)/1.0e9); } public abstract int countPrimes(final int max); }
上面是一個抽象類,大體就是定義了幾種求解素數的工具,求一個範圍的素數,還有計時,還有求解整個範圍素數的方法,而這個方法是抽象的也是咱們須要實現的,也是並行和非並行的不一樣之處.負載均衡
順序執行的方法:ide
class primeshunxu extends PrimeAbstract{ @Override public int countPrimes(int max) { // TODO Auto-generated method stub return countPrimeInRange(0, max); } }
很簡單,已經有現成的工具了嘛,直接用就好.工具
並行的方法:性能
並行的精髓是作任務分割,具體怎麼分割上面其實已經表現出來了,講整個求解範圍進行分割,而後把求解結果累加便可.下面是代碼spa
import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Prime extends PrimeAbstract{ @Override public int countPrimes(int max) { // TODO Auto-generated method stub final int poolsize = (int) (Runtime.getRuntime().availableProcessors()); final ExecutorService executorpool = Executors.newFixedThreadPool(poolsize); final List<Callable<Integer>> partition = new ArrayList<>(); int total = 0; int tasks =poolsize; int per = max/tasks; int pre = 0; for(int j = 1; j<=tasks; j++){ final int low = pre+1; final int up; if(low+per>max){ up = max; }else{ up = low+per; } partition.add(new Callable<Integer>() { @Override public Integer call() throws Exception { // TODO Auto-generated method stub return countPrimeInRange(low, up); } }); pre = up; } try { List<Future<Integer>> result = executorpool.invokeAll(partition); executorpool.shutdown(); for(Future<Integer> i : result){ total += i.get(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return total; } }
最後調用線程
public static void main(String[] args) { // TODO Auto-generated method stub int max = 3000000; new Prime().timeAndCompute(max); new primeshunxu().timeAndCompute(max); }
如今我看看下運行對比code
小於3000000的素數的個數爲216816花費的時間6.464452757
小於3000000的素數的個數爲216816花費的時間12.226215544blog
能夠看到並行的時間是順序執行的一半,雖然代碼上覆雜了不少,可是效果仍是比較明顯的,這裏就涉及到了poolsize和tasks的個數問題:
根據poolsize計算公式,對於計算密集型任務,阻塞係數是0,那麼poolsize大小設置成和cpu核數相等便可,由於假如設置大了之後,cpu就會掛起一個非阻塞線程,而後去執行另外一個非阻塞任務,這樣作意義不大,並且會代碼額外的上下文切換開銷.也就是在cpu密集型任務中,經過將線程數增多到比核數還多,是無效的.
那麼剩下的就是tasks了.
咱們首先看tasks = poolsize的狀況,也就是將每一個線程執行一個任務,而後就結束
看下分析結果
執行輸出
小於3000000的素數的個數爲216816花費的時間6.24529681
能夠看到 有四個線程,由於本機的核數爲4,而後其中三個線程在運行完畢後進入了駐留狀態,而還有一個線程從開始到最後一直在執行,也就是那個數值分配最大的那個線程,因爲計算花費的時間更多,因此最好只有這單個線程執行了.因此運行時間也就是運行時間最長的那個線程所花的時間
那麼咱們能夠想到,這樣來分配任務的話,形成了四個線程的負載時不均衡的,計算量最大的線程是時間瓶頸,那麼咱們能夠怎麼作呢?
1.在任務數不變的狀況下從新劃分,能夠想象前面12.5%的數計算量很小,後12.5%計算量很大,這前面的八分之一和後面的八分之一組合成一個任務,這樣來進行均衡,可是這樣作要對問題自己的行爲要有一個很是深入的理解,實現相對麻煩
2.增長任務數,咱們能夠想象,當咱們任務數變多的時候,每次四個線程上跑的都是時間花銷差很少的任務,這樣一來,咱們就可以作到任務均衡了.可是具體增長到多少呢?
咱們增長到100個任務
運行結果:
小於3000000的素數的個數爲216816花費的時間5.921416693
能夠看到,有了一些提高,並且每一個線程從開始到最後都是滿負載的,作到了負載均衡.這裏雖然沒有理論上的達到順序執行的四分之一,也就是四秒左右,可是已經很客觀了.
關於poolsize和tasks的總結:
1.子任務的劃分數是不能小於處理器的核心數,不然性能都不好的.
2.對於計算密集型任務,線程數多於cpu核心數對性能提高是毫無幫助的.
3.在子任務數超過必定的數量後,再繼續增長的話,對性能提高是十分有限的