Java併發和多執行緒2:3種方式實現陣列求和
本篇演示3個陣列求和的例子。
例子1:單執行緒
例子2:多執行緒,同步求和(如果沒有計算完成,會阻塞)
例子3:多執行緒,非同步求和(先累加已經完成的計算結果)
例子1-程式碼
例子2-程式碼
ExecutoreService提供了submit()方法,傳遞一個Callable,或Runnable,返回Future。如果Executor後臺執行緒池還沒有完成Callable的計算,這呼叫返回Future物件的get()方法,會阻塞直到計算完成。
例子3-程式碼
在剛在的例子中,getResult()方法的實現過程中,迭代了FutureTask的陣列,如果任務還沒有完成則當前執行緒會阻塞。
如果我們希望任意字任務完成後就把其結果加到result中,而不用依次等待每個任務完成,可以使CompletionService。
生產者submit()執行的任務。使用者take()已完成的任務,並按照完成這些任務的順序處理它們的結果 。也就是呼叫CompletionService的take方法是,會返回按完成順序放回任務的結果。
CompletionService內部維護了一個阻塞佇列BlockingQueue,如果沒有任務完成,take()方法也會阻塞。
修改剛才的例子2,使用CompletionService:
執行程式碼
控制檯輸出
單執行緒:93.0
Sum:20000000100000000
subSum=3750000175000002,result=3750000175000002
subSum=1250000075000001,result=5000000250000003
subSum=6250000275000003,result=11250000525000006
subSum=8749999574999994,result=20000000100000000
Sum:20000000100000000
多核平行計算,非同步相加:786.0
20000000100000000
多核平行計算,同步相加:650.0
個人看法:3段程式碼的時間僅供參考,沒有排除干擾因素。
總的來說,單執行緒執行更快一些,應該是由於“陣列求和”本身,並不需要其它額外資源,不會阻塞。
而多執行緒,反而增加了“執行緒排程”的時間開銷。
還可以看出,CPU計算還是非常快的。“200000000”2億個整數相加,用了不到0.1秒的時間。
插曲
最開始看程式碼的時候,誤解了。以為“根據CPU核心個數拆分任務”,這個時候的“多執行緒”就是“並行”了。
實際上,不一定,除了要看CPU的核數,還要看作業系統的分配。
// 根據CPU核心個數拆分任務,建立FutureTask並提交到Executor
for (int i = 0; i < cpuCoreNumber; i++) {
}
最開始,我還在考慮“單執行緒”、“多核並行+多執行緒併發”、“單核+多執行緒併發”,等好幾種情況來實現“陣列求和”。
最後,感覺自己還是想多了。“並行”應該不受自己控制,只能控制是“單執行緒”或者“多執行緒”。
“java併發程式設計-Executor框架”這篇文章中的“例子:平行計算陣列的和。” 這句話,誤導了我,根本不能保證是“平行計算”。
友情提示:網路上的文章,僅供參考學習,需要自己的判斷。
關於Java-多核-並行-多執行緒,我初步認為“多執行緒可以並行執行,但不受我們自己的控制,取決於作業系統”。
網友的一些看法:
看法1:
java執行緒可以在執行在多個cpu核上嗎?
我是一直都以為這個問題的答案是肯定的,也就是說可以執行在多核上。
但是有一天見到這樣的一個理論,我就頓時毀三觀了。
JVM在作業系統中是作為一個程式的,java所有的執行緒都執行自這個JVM程式中,
所以說java執行緒某個時間只可能執行在一個核上。
這個說法對我的打擊太大了,我不能接受。於是就開始多方求證。網上搜尋 和朋友一起討論,
最終證實了java執行緒是可以執行在多核上的,為什麼呢?
下面一句話將驚醒夢中人:
現代os都將執行緒作為最小排程單位,程式作為資源分配的最小單位。 在windows中程式是不活動的,
只是作為執行緒的容器。
也就是說,java中的所有執行緒確實在JVM程式中,但是CPU排程的是程式中的執行緒。
看法2:
JAVA中的多執行緒能在多CPU機器上並行執行嗎?注意,我說的不是併發執行哦 。
我們用java寫一個多執行緒程式,就啟動了一個JVM程式,所以這些執行緒都是在這一個JVM程式之中的,我不知道同一時刻,能不能有多個CPU執行同一程式,進而並行執行這同一程式中的不同執行緒?一直很疑惑
你的思路是對的,CPU就是為了迎合作業系統的多執行緒從而提高系統的計算效率.但是具體分配任務到各個核心中去執行的並非JAVA與JVM而是作業系統.
也就是說,你所執行的多執行緒,可能會被分配到同一個CPU核心中執行.也可能非配到不同的cpu中執行.如果可以控制CPU的分配,那也應該是作業系統的api才能實現的了。
我用JAVA建立了一個執行緒,這時候有主執行緒和子執行緒都在執行,那意思雙核CPU有可能在同一時刻點並行執行這兩個執行緒咯?
我翻了好多JAVA的有關多執行緒的章節,似乎都沒有說道多核CPU執行JAVA多執行緒,貌似都是已單核為例講解的,所以我一直覺得可能都是併發的而不是並行的?
不是,你要將你的軟體執行緒和計算機的CPU處理執行緒區分開呀.簡單說,你是無法控制CPU對於任務的分配的.
更多程式碼示例:
http://git.oschina.net/fansunion/Concurrent(逐步更新中)
參考資料:
java併發程式設計-Executor框架
http://www.iteye.com/topic/366591
java執行緒可以在執行在多個cpu核上嗎?
http://blog.csdn.net/maosijunzi/article/details/42527553
JAVA中的多執行緒能在多CPU上並行執行嗎?注意,我說的不是併發執行哦
http://zhidao.baidu.com/link?url=e11sEOSNFoLTfVyP-5FfpktIXEgbMQkbLAzvgh8mn4V16n_qQas89voj5gVhOEkho0jRA7fp_vbnElxKgeQCDrOxGkcu6xAWaUniqpcWg33
例子1:單執行緒
例子2:多執行緒,同步求和(如果沒有計算完成,會阻塞)
例子3:多執行緒,非同步求和(先累加已經完成的計算結果)
例子1-程式碼
package cn.fansunion.executorservice;
public class BasicCaculator {
public static long sum(int[] numbers){
long sum = 0;
for(int i=0;i<numbers.length;i++){
sum += numbers[i];
}
return sum;
}
}
例子2-程式碼
ExecutoreService提供了submit()方法,傳遞一個Callable,或Runnable,返回Future。如果Executor後臺執行緒池還沒有完成Callable的計算,這呼叫返回Future物件的get()方法,會阻塞直到計算完成。
package cn.fansunion.executorservice;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
//併發計算陣列的和,“同步”求和
public class ConcurrentCalculator {
private ExecutorService exec;
//這個地方,純粹是“一廂情願”,“並行執行”不受我們們控制,取決於作業系統的“態度”
private int cpuCoreNumber;
private List<Future<Long>> tasks = new ArrayList<Future<Long>>();
class SumCalculator implements Callable<Long> {
private int[] numbers;
private int start;
private int end;
public SumCalculator(final int[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
}
public Long call() throws Exception {
Long sum = 0L;
for (int i = start; i < end; i++) {
sum += numbers[i];
}
return sum;
}
}
public ConcurrentCalculator() {
cpuCoreNumber = Runtime.getRuntime().availableProcessors();
exec = Executors.newFixedThreadPool(cpuCoreNumber);
}
public Long sum(final int[] numbers) {
// 根據CPU核心個數拆分任務,建立FutureTask並提交到Executor
for (int i = 0; i < cpuCoreNumber; i++) {
int increment = numbers.length / cpuCoreNumber + 1;
int start = increment * i;
int end = increment * i + increment;
if (end > numbers.length)
end = numbers.length;
SumCalculator subCalc = new SumCalculator(numbers, start, end);
FutureTask<Long> task = new FutureTask<Long>(subCalc);
tasks.add(task);
if (!exec.isShutdown()) {
exec.submit(task);
}
}
return getResult();
}
/**
* 迭代每個只任務,獲得部分和,相加返回
*/
public Long getResult() {
Long result = 0l;
for (Future<Long> task : tasks) {
try {
// 如果計算未完成則阻塞
Long subSum = task.get();
result += subSum;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return result;
}
public void close() {
exec.shutdown();
}
}
例子3-程式碼
在剛在的例子中,getResult()方法的實現過程中,迭代了FutureTask的陣列,如果任務還沒有完成則當前執行緒會阻塞。
如果我們希望任意字任務完成後就把其結果加到result中,而不用依次等待每個任務完成,可以使CompletionService。
生產者submit()執行的任務。使用者take()已完成的任務,並按照完成這些任務的順序處理它們的結果 。也就是呼叫CompletionService的take方法是,會返回按完成順序放回任務的結果。
CompletionService內部維護了一個阻塞佇列BlockingQueue,如果沒有任務完成,take()方法也會阻塞。
修改剛才的例子2,使用CompletionService:
package cn.fansunion.executorservice;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//併發計算陣列的和,“非同步”求和
public class ConcurrentCalculatorAsync {
private ExecutorService exec;
private CompletionService<Long> completionService;
//這個地方,純粹是“一廂情願”,“並行執行”不受我們們控制,取決於作業系統的“態度”
private int cpuCoreNumber;
class SumCalculator implements Callable<Long> {
private int[] numbers;
private int start;
private int end;
public SumCalculator(final int[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
}
public Long call() throws Exception {
Long sum = 0l;
for (int i = start; i < end; i++) {
sum += numbers[i];
}
return sum;
}
}
public ConcurrentCalculatorAsync() {
cpuCoreNumber = Runtime.getRuntime().availableProcessors();
exec = Executors.newFixedThreadPool(cpuCoreNumber);
completionService = new ExecutorCompletionService<Long>(exec);
}
public Long sum(final int[] numbers) {
// 根據CPU核心個數拆分任務,建立FutureTask並提交到Executor
for (int i = 0; i < cpuCoreNumber; i++) {
int increment = numbers.length / cpuCoreNumber + 1;
int start = increment * i;
int end = increment * i + increment;
if (end > numbers.length){
end = numbers.length;
}
SumCalculator subCalc = new SumCalculator(numbers, start, end);
if (!exec.isShutdown()) {
completionService.submit(subCalc);
}
}
return getResult();
}
/**
* 迭代每個只任務,獲得部分和,相加返回
*/
public Long getResult() {
Long result = 0l;
for (int i = 0; i < cpuCoreNumber; i++) {
try {
Long subSum = completionService.take().get();
result += subSum;
System.out.println("subSum="+subSum+",result="+result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return result;
}
public void close() {
exec.shutdown();
}
}
執行程式碼
package cn.fansunion.executorservice;
import java.math.BigDecimal;
//陣列求和3個Demo
public class ArraySumDemo {
public static void main(String[] args) {
int n = 200000000;
int[] numbers = new int[n];
for(int i=1;i<=n;i++){
numbers[i-1]=i;
}
basic(numbers);
long time = System.currentTimeMillis();
concurrentCaculatorAsync(numbers);
long endTime=System.currentTimeMillis();
System.out.println("多核平行計算,非同步相加:"+time(time,endTime));
long time2 = System.currentTimeMillis();
concurrentCaculator(numbers);
long endTime2=System.currentTimeMillis();
System.out.println("多核平行計算,同步相加:"+time(time2,endTime2));
}
private static void basic(int[] numbers) {
long time1 = System.currentTimeMillis();
long sum=BasicCaculator.sum(numbers);
long endTime1 = System.currentTimeMillis();
System.out.println("單執行緒:"+time(time1,endTime1));
System.out.println("Sum:"+sum);
}
private static double time(long time, long endTime) {
long costTime = endTime-time;
BigDecimal bd = new BigDecimal(costTime);
//本來想著,把毫秒轉換成秒的,最後發現計算太快了
BigDecimal unit = new BigDecimal(1L);
BigDecimal s= bd.divide(unit,3);
return s.doubleValue();
}
//平行計算,“同步”求和
private static void concurrentCaculator(int[] numbers) {
ConcurrentCalculator calc = new ConcurrentCalculator();
Long sum = calc.sum(numbers);
System.out.println(sum);
calc.close();
}
//平行計算,“非同步”求和
private static void concurrentCaculatorAsync(int[] numbers) {
ConcurrentCalculatorAsync calc = new ConcurrentCalculatorAsync();
Long sum = calc.sum(numbers);
System.out.println("Sum:"+sum);
calc.close();
}
}
控制檯輸出
單執行緒:93.0
Sum:20000000100000000
subSum=3750000175000002,result=3750000175000002
subSum=1250000075000001,result=5000000250000003
subSum=6250000275000003,result=11250000525000006
subSum=8749999574999994,result=20000000100000000
Sum:20000000100000000
多核平行計算,非同步相加:786.0
20000000100000000
多核平行計算,同步相加:650.0
個人看法:3段程式碼的時間僅供參考,沒有排除干擾因素。
總的來說,單執行緒執行更快一些,應該是由於“陣列求和”本身,並不需要其它額外資源,不會阻塞。
而多執行緒,反而增加了“執行緒排程”的時間開銷。
還可以看出,CPU計算還是非常快的。“200000000”2億個整數相加,用了不到0.1秒的時間。
插曲
最開始看程式碼的時候,誤解了。以為“根據CPU核心個數拆分任務”,這個時候的“多執行緒”就是“並行”了。
實際上,不一定,除了要看CPU的核數,還要看作業系統的分配。
// 根據CPU核心個數拆分任務,建立FutureTask並提交到Executor
for (int i = 0; i < cpuCoreNumber; i++) {
}
最開始,我還在考慮“單執行緒”、“多核並行+多執行緒併發”、“單核+多執行緒併發”,等好幾種情況來實現“陣列求和”。
最後,感覺自己還是想多了。“並行”應該不受自己控制,只能控制是“單執行緒”或者“多執行緒”。
“java併發程式設計-Executor框架”這篇文章中的“例子:平行計算陣列的和。” 這句話,誤導了我,根本不能保證是“平行計算”。
友情提示:網路上的文章,僅供參考學習,需要自己的判斷。
關於Java-多核-並行-多執行緒,我初步認為“多執行緒可以並行執行,但不受我們自己的控制,取決於作業系統”。
網友的一些看法:
看法1:
java執行緒可以在執行在多個cpu核上嗎?
我是一直都以為這個問題的答案是肯定的,也就是說可以執行在多核上。
但是有一天見到這樣的一個理論,我就頓時毀三觀了。
JVM在作業系統中是作為一個程式的,java所有的執行緒都執行自這個JVM程式中,
所以說java執行緒某個時間只可能執行在一個核上。
這個說法對我的打擊太大了,我不能接受。於是就開始多方求證。網上搜尋 和朋友一起討論,
最終證實了java執行緒是可以執行在多核上的,為什麼呢?
下面一句話將驚醒夢中人:
現代os都將執行緒作為最小排程單位,程式作為資源分配的最小單位。 在windows中程式是不活動的,
只是作為執行緒的容器。
也就是說,java中的所有執行緒確實在JVM程式中,但是CPU排程的是程式中的執行緒。
看法2:
JAVA中的多執行緒能在多CPU機器上並行執行嗎?注意,我說的不是併發執行哦 。
我們用java寫一個多執行緒程式,就啟動了一個JVM程式,所以這些執行緒都是在這一個JVM程式之中的,我不知道同一時刻,能不能有多個CPU執行同一程式,進而並行執行這同一程式中的不同執行緒?一直很疑惑
你的思路是對的,CPU就是為了迎合作業系統的多執行緒從而提高系統的計算效率.但是具體分配任務到各個核心中去執行的並非JAVA與JVM而是作業系統.
也就是說,你所執行的多執行緒,可能會被分配到同一個CPU核心中執行.也可能非配到不同的cpu中執行.如果可以控制CPU的分配,那也應該是作業系統的api才能實現的了。
我用JAVA建立了一個執行緒,這時候有主執行緒和子執行緒都在執行,那意思雙核CPU有可能在同一時刻點並行執行這兩個執行緒咯?
我翻了好多JAVA的有關多執行緒的章節,似乎都沒有說道多核CPU執行JAVA多執行緒,貌似都是已單核為例講解的,所以我一直覺得可能都是併發的而不是並行的?
不是,你要將你的軟體執行緒和計算機的CPU處理執行緒區分開呀.簡單說,你是無法控制CPU對於任務的分配的.
更多程式碼示例:
http://git.oschina.net/fansunion/Concurrent(逐步更新中)
參考資料:
java併發程式設計-Executor框架
http://www.iteye.com/topic/366591
java執行緒可以在執行在多個cpu核上嗎?
http://blog.csdn.net/maosijunzi/article/details/42527553
JAVA中的多執行緒能在多CPU上並行執行嗎?注意,我說的不是併發執行哦
http://zhidao.baidu.com/link?url=e11sEOSNFoLTfVyP-5FfpktIXEgbMQkbLAzvgh8mn4V16n_qQas89voj5gVhOEkho0jRA7fp_vbnElxKgeQCDrOxGkcu6xAWaUniqpcWg33
相關文章
- 3種方式實現python多執行緒併發處理Python執行緒
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- 【多執行緒高併發程式設計】二 實現多執行緒的幾種方式執行緒程式設計
- 多執行緒-多執行緒方式3的求和案例執行緒
- Java實現多執行緒的三種方式Java執行緒
- Java多執行緒實現方式Java執行緒
- Java 併發和多執行緒(一) Java併發性和多執行緒介紹[轉]Java執行緒
- Java建立多執行緒的幾種方式實現Java執行緒
- 【java】【多執行緒】建立執行緒的兩種常用方式(2)Java執行緒
- Java併發和多執行緒:序Java執行緒
- Java併發和多執行緒4:使用通用同步工具CountDownLatch實現執行緒等待Java執行緒CountDownLatch
- JAVA多執行緒併發Java執行緒
- java中多執行緒併發的處理方式Java執行緒
- JAVA多執行緒和併發基礎Java執行緒
- 用多執行緒,實現併發,TCP執行緒TCP
- Java併發和多執行緒3:執行緒排程和有條件取消排程Java執行緒
- Java多執行緒之—Synchronized方式和CAS方式實現執行緒安全效能對比Java執行緒synchronized
- java 多執行緒 併發 面試Java執行緒面試
- Map實現執行緒安全的3種方式執行緒
- Java多執行緒【三種實現方法】Java執行緒
- Java併發和多執行緒1:併發框架基本示例Java執行緒框架
- Java多執行緒和併發問題集Java執行緒
- 關於多執行緒的兩種實現方式執行緒
- Java併發(三)----建立執行緒的三種方式及檢視程式執行緒Java執行緒
- java執行緒實現方式Java執行緒
- Java建立多執行緒的四種方式Java執行緒
- Java多執行緒--併發和並行的區別Java執行緒並行
- java多執行緒與併發 - 併發工具類Java執行緒
- java多執行緒與併發 - 執行緒池詳解Java執行緒
- Java多執行緒/併發08、中斷執行緒 interrupt()Java執行緒
- iOS併發機制(三) —— NSOperation實現多併發之建立佇列和開啟執行緒iOS佇列執行緒
- Java進階篇:多執行緒併發實踐Java執行緒
- 多執行緒-多執行緒方式3的思路及程式碼實現:方式3依賴於執行緒池存在的執行緒
- 多執行緒-多執行緒方式2的思路及程式碼實現執行緒
- shell佇列實現執行緒併發控制(轉)佇列執行緒
- 【Python】python 多執行緒兩種實現方式Python執行緒
- 10、Java併發性和多執行緒-執行緒安全與不可變性Java執行緒
- Java多執行緒/併發12、多執行緒訪問static變數Java執行緒變數