最近在看《Java虛擬機並發編程》,在此記錄一些重要的東東。
線程數的確定:
1. 獲取系統可用的處理器核心數:int numOfCores = Runtime.getRuntime().availableProcessors()
2. 如果任務是計算密集型的,則線程數 = numOfCores
如果任務是IO密集型的,則線程數 = numOfCores / (1 - 阻塞系數), 其中阻塞系數在0~1之間。
注:如果任務被阻塞的時間大於執行時間, 則這些任務是IO密集型的,我們就需要創建比處理器核心數大幾倍數量的線程
在解決問題的過程中使處理器一直保持忙碌狀態比將負載均攤到每個子任務要實惠得多。
任務完成並不代表線程消亡。
計算密集型任務:如求1到10000000內所有素數的個數
1. AbstractPrimeFinder
public abstract class AbstractPrimeFinder { public boolean isPrime(final int number){ if(number <= 1) return false; for(int i = 2; i <=Math.sqrt(number); i++){ if (number % i == 0) return false; } return true; } public int countPrimesInRange(final int lower, final int upper){ int total = 0; for( int i = lower; i <= upper; i++){ if(isPrime(i)) total++; } return total; } public void timeAndComputer(final int number){ long start = System.nanoTime(); int numberOfPrimes = countPrimes(number); long end = System.nanoTime(); System.out.printf("Number of primes under %d is %d\n", number, numberOfPrimes); System.out.println("Spend time(seconds) is " + (end-start)/1.0e9); } public abstract int countPrimes(final int number); }
2. ConcurrentPrimeFinder
/** * 對於計算密集型的任務,增加線程數並沒有什么意義,線程數應該等於CPU內核數。如果較難把任務均攤到CPU,則 * 可以把任務切分成較多塊,以確保CPU完成某塊任務后,可以繼續處理其它塊。防止某個CPU完成任務后處於空閑狀態。 * @author shj * */ public class ConcurrentPrimeFinder extends AbstractPrimeFinder{ private final int poolSize; private final int numberOfParts; public ConcurrentPrimeFinder(int poolSize, int numberOfParts){ this.poolSize = poolSize; this.numberOfParts = numberOfParts; } @Override public int countPrimes(final int number) { int count = 0 ; try{ List<Callable<Integer>> partitions = new ArrayList<>(); int chunksPerPartition = number / numberOfParts; for(int i = 0; i < numberOfParts; i++){ final int lower = (i * chunksPerPartition) + 1; final int upper = (i == numberOfParts - 1) ? number : lower + chunksPerPartition - 1; partitions.add(new Callable<Integer>(){ public Integer call(){ return countPrimesInRange(lower, upper); } }); } ExecutorService executorPool = Executors.newFixedThreadPool(poolSize); List<Future<Integer>> results = executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS); executorPool.shutdown(); for(Future<Integer> result : results){ count += result.get(); } }catch(Exception e){ e.printStackTrace(); } return count; } public static void main(String[] args){ int cores = Runtime.getRuntime().availableProcessors(); int numberOfParts = 20; //划分成子區間的數量, 修改此值查看運行時間的變化 new ConcurrentPrimeFinder(cores,numberOfParts).timeAndComputer(10_000_000); } }