有這樣一種場景,用多線程發送數據到某個服務器,須要知道各個線程是否都發送成功,等全部線程都發送完成才能繼續下一輪計算和發送。若是用傳統的多線程方式,就須要啓動多個線程,而後在每一個線程中分別發送數據,外部經過某種方式等待各個線程所有都發送完成,再進行後面的計算等流程。這種實現方式的代碼會比較臃腫,在java中提供了一種Callable+Future的方法,能夠將異步的多線程調用變爲同步方式。java
Callable編程
在java的多線程編程中,有Thread和Runnable兩種方式來新建線程,其中Runnable封裝了一個異步運行的任務,能夠認爲是一個沒有任何參數和返回值的異步方法。Callable接口相似於Runnable,二者都是爲那些其實例可能被另外一個線程執行的類設計的,不一樣之處在於:服務器
Runnable不會返回結果,而且沒法拋出通過檢查的異常。而Callable是有返回結果而且可能拋出異常的。多線程
Runnable定義了run方法,而Callable定義了一個不帶任何參數的叫作call的方法。異步
此外,Callable接口的類型參數也是返回值的類型。ide
public interface Callable {this
/**.net
* Computes a result, or throws an exception if unable to do so.線程
*設計
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Future
Future表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。計算完成後只能使用 get 方法來獲取結果,若有必要,計算完成前能夠阻塞此方法。取消則由 cancel 方法來執行。還提供了其餘方法,以肯定任務是正常完成仍是被取消了。一旦計算完成,就不能再取消計算。若是爲了可取消性而使用 Future 但又不提供可用的結果,則能夠聲明 Future<?> 形式類型、並返回 null 做爲底層任務的結果。
當使用Future對象時,你就能夠啓動一個計算,把計算結果給某線程,而後就去幹本身的事。Future對象的全部者在結果計算好以後就能夠獲得它。
public interface Future {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask
FutureTask包裝器是一種很方便地將Callable轉換爲Future和Runnable的機制,它同時實現了二者的接口。例如:
Callable comput = …;
FutureTask task = new FutureTask(comput);
Thread t = new Thread(task); // it's a Runnable
t.start();
…
Integer result = t.get(); // it’s a Future
因此可以使用FutureTask包裝Callable或Runnable對象。因爲FutureTask實現了Runnable,可將 FutureTask提交給線程池的執行器類Executor執行。
使用Future+Callable+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;
public class FutureTest {
public static class Task implements Callable {
private long id;
public Task(long id){
this.id = id;
}
@Override
public String call() throws Exception {
System.out.printf("Thread#%s : in call\n", this.id);
return this.id + "";
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
List results = new ArrayList();
ExecutorService execService = Executors.newCachedThreadPool();
for(int i=0; i<10;i++)
results.add(execService.submit(new Task(i)));
for(Future res : results)
System.out.println(res.get());
}
}
可見,使用Future+Callable模式對於某些須要等待多個線程執行結果的場景很是有用,在HBase的batch操做中就使用了這種模式。