先創(chuàng)建一個task
static class TestTask implements Callable<String>{
int i;
TestTask(int i){
this.i = i;
}
@Override
public String call() throws Exception {
int time = new Random().nextInt(5000);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任務(wù):" + i + " finished:" + " " + time;
}
}
接著創(chuàng)建2測試方法:
private static void executorServiceTest(){
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<FutureTask> tasks = new ArrayList<>();
for(int i =0; i<10; i++){
FutureTask f = new FutureTask(new TestTask(i));
tasks.add(f);
}
tasks.forEach(task -> {
executorService.submit(task);
});
tasks.forEach(task ->{
try {
System.out.println(task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
}
運行結(jié)果:
任務(wù):0 finished: 38
任務(wù):1 finished: 2563
任務(wù):2 finished: 3255
任務(wù):3 finished: 4034
任務(wù):4 finished: 2158
任務(wù):5 finished: 4504
任務(wù):6 finished: 2598
任務(wù):7 finished: 3072
任務(wù):8 finished: 4668
任務(wù):9 finished: 2695
private static void completionServiceTest() {
ExecutorService threadPool1 = Executors.newFixedThreadPool(10);
CompletionService<String> completionService = new ExecutorCompletionService<String>(threadPool1);
for(int i =0;i<10;i++) {
completionService.submit(new TestTask(i));
}
for(int j=0;j<10;j++) {
try {
System.out.println(completionService.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
任務(wù):4 finished: 341
任務(wù):0 finished: 1222
任務(wù):2 finished: 1936
任務(wù):9 finished: 2656
任務(wù):6 finished: 3042
任務(wù):1 finished: 3088
任務(wù):8 finished: 3608
任務(wù):3 finished: 3999
任務(wù):5 finished: 4361
任務(wù):7 finished: 4789
結(jié)論:
這兩者最主要的區(qū)別在于submit的task不一定是按照加入自己維護的list順序完成的。
從list中遍歷的每個Future對象并不一定處于完成狀態(tài)曲横,這時調(diào)用get()方法就會被阻塞住
喂柒,如果系統(tǒng)是設(shè)計成每個線程完成后就能根據(jù)其結(jié)果繼續(xù)做后面的事,
這樣對于處于list后面的但是先完成的線程就會增加了額外的等待時間禾嫉。
而CompletionService的實現(xiàn)是維護一個保存Future對象的BlockingQueue灾杰。只有當(dāng)這個Future對象狀態(tài)是結(jié)束的時候,
才會加入到這個Queue中熙参,take()方法其實就是Producer-Consumer中的Consumer艳吠。它會從Queue中取出Future對象,
如果Queue是空的尊惰,就會阻塞在那里讲竿,直到有完成的Future對象加入到Queue中。
所以弄屡,先完成的必定先被取出题禀。這樣就減少了不必要的等待時間