在併發處理數據時,一個典型的場景是: 咱們將須要處理的而且很是耗時的任務(例如訪問網絡IO)交給線程池中的一個線程去處理,咱們當前的線程執行其餘的任務,而後當交給線程池的任務執行完成後,咱們再獲取執行的結果,對獲得的結果進處理,首選會充分利用CPU的多核資源,其次是將會阻塞的任務提早進行提交處理,儘可能減小系統卡頓。(在互聯網公司這樣網絡訪問的方式很是常見。) 那麼今天咱們來談一種新的實現方式Future<V>接口Callable<V>接口java
Future<V>接口Callable<V>網絡
在Future接口中聲明瞭5個方法,下面依次解釋每一個方法的做用:併發
在callable接口中聲明瞭1個方法,call()方法,主要用於執行你的操做,並放回操做後的值。 舉個栗子 Future接口和Callable接口常常配合使用,廢話很少說,上代碼。dom
場景一ide
該場景只是簡單的對Future接口和Callable接口進行使用介紹,注意Future接口的get()方法會阻塞,直到線程池中的線程將數據處理完成,才執行後面的操做。函數
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class FutrueTest { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor();// 單線程,線程池 Future<Long> myFuturetask = executor.submit(new MyFutureTask());// 提交任務 System.out.println("get before"); long result = myFuturetask.get();// 這個地方會阻塞 System.out.println("get after"); System.out.println("get result is " + result); } } class MyFutureTask implements Callable<Long> { [@Override](https://my.oschina.net/u/1162528) public Long call() throws Exception { long result = 1; System.out.println("future task start"); long max = new Random().nextInt(10); for (int i = 1; i <= max; i++)//計算階乘 { result *= i; } TimeUnit.SECONDS.sleep(10); System.out.println("future task end"); return result; } }
執行結果this
get before future task start future task end get after get result is 720
場景二.net
如今有這樣一個需求,就是一會兒提交不少的FutureTask到線程池中,而後我等待處理的結果,此時我要作的是:哪一個FutureTask先處理完成,我就先處理其獲得的結果。問題的難度在於:每一個提交到線程池中的FutureTask處理的時間都是不同的,我怎麼來的得知那個FutureTask線程先處理完呢? 題外話:仔細想一想:用Join()、CountDownLatch類、CyclicBarrier類是否是感受都不太好實現?考慮了一下直接Runnable接口實現呢,是否是也會很麻煩?!後續會給出對比~ 廢話很少說,直接上代碼:線程
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class FutrueTest { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newCachedThreadPool();// 多個線程的線程池 List<Future<MyResult>> list = new ArrayList<Future<MyResult>>(); for (int i = 1; i <= 6; i++) { list.add(executor.submit(new MyFutureTask(String.valueOf(i))));//提交任務 } System.out.println("do your something"); TimeUnit.SECONDS.sleep(3); int rank = 1; while (true)//一直訪問,直到task 列表中的task 爲零 { if (list.size() <= 0) { break; } Iterator<Future<MyResult>> iterator = list.iterator(); while (iterator.hasNext())//循環訪問task { Future<MyResult> f = iterator.next(); if (f.isDone())//task 是否完成,若是完成則獲取值,若是 { MyResult result = f.get(); System.out.println("------------------>my rank is " + rank++ + " future task is " + result.name + " result is " + result.result); iterator.remove(); } } } } } class MyFutureTask implements Callable<MyResult> { private String name; public MyFutureTask(String name) { this.name = name; } [@Override](https://my.oschina.net/u/1162528) public MyResult call() throws Exception { long result = 1; long max = new Random().nextInt(10); for (int i = 1; i <= max; i++)// 計算階乘 { result *= i; } TimeUnit.SECONDS.sleep(new Random().nextInt(10)); System.out.println(name + " future task result " + result); return new MyResult(name, result); } } class MyResult { String name;//記錄任務ID long result;//結果 public MyResult(String name, long result) { super(); this.name = name; this.result = result; } }
運行結果:code
do your something 6 future task result 1 5 future task result 6 ------------------>my rank is 1future task is 5 result is 6 ------------------>my rank is 2future task is 6 result is 1 2 future task result 40320 ------------------>my rank is 3future task is 2 result is 40320 1 future task result 6 4 future task result 720 ------------------>my rank is 4future task is 1 result is 6 ------------------>my rank is 5future task is 4 result is 720 3 future task result 24 ------------------>my rank is 6future task is 3 result is 24
這個地方主要注意這個isDown()這個函數來判斷提交的Future任務是否執行完成,若是完成就獲取任務結果作後續的處理。 可是咱們發現,存在一個問題就是:並無真的像我想一想的那樣,哪一個任務先執行完,我就先處理哪一個,難道就真的沒有辦法了麼?! 看下面代碼:
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class FutrueTest { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newCachedThreadPool();// 多個線程的線程池 CompletionService<MyResult> completionService = new ExecutorCompletionService<MyResult>(executor);//主要是這個類 for (int i = 1; i <= 6; i++) { completionService.submit(new MyFutureTask(String.valueOf(i)));// 提交任務 } System.out.println("do your something"); TimeUnit.SECONDS.sleep(3);//休眠,也能夠不休眠 int rank = 0; for (int i = 1; i <= 6; i++) { MyResult result = completionService.take().get();// 會阻塞哦 System.out.println("------------------>my rank is " + rank++ + " future task is " + result.name + " result is " + result.result); } } } class MyFutureTask implements Callable<MyResult> { private String name; public MyFutureTask(String name) { this.name = name; } [@Override](https://my.oschina.net/u/1162528) public MyResult call() throws Exception { long result = 1; long max = new Random().nextInt(10); for (int i = 1; i <= max; i++)// 計算階乘 { result *= i; } TimeUnit.SECONDS.sleep(new Random().nextInt(10)); System.out.println(name + " future task result " + result); return new MyResult(name, result); } } class MyResult { String name;// 記錄任務ID long result;// 結果 public MyResult(String name, long result) { super(); this.name = name; this.result = result; } }
運行結果
do your something 3 future task result 24 1 future task result 6 4 future task result 1 ------------------>my rank is 0 future task is 3 result is 24 ------------------>my rank is 1 future task is 1 result is 6 ------------------>my rank is 2 future task is 4 result is 1 5 future task result 720 6 future task result 40320 ------------------>my rank is 3 future task is 5 result is 720 ------------------>my rank is 4 future task is 6 result is 40320 2 future task result 2 ------------------>my rank is 5 future task is 2 result is 2 終於獲得了咱們想要的結果。
總結 Callable接口和Runnable接口在某些狀況下均可以提交一個本身的任務給線程池來執行。
區別在於:
1.Callable接口能夠有返回值,而Runnable接口沒有。(固然不是絕對的看你怎麼實現)
2.Callable接口實現對象經過ExecutorService接口的submit()方法提交到線程池,而Runnable接口實現對象經過Executor接口的execute()方法提交大線程池,固然ExecutorService接口繼承Executor接口
至於他們的使用,看場景