java Callable & Future & FutureTask

        實現Runnable接口的線程類與一個缺陷,就是在任務執行完以後沒法取得任務的返回值。 若是須要獲取執行結果,就必須經過共享變量或者使用線程通訊的方式來達到效果,這樣使用起來就比較麻煩 。因此,從JDK 1.5開始,java提供了Callable接口,該接口和Runnable接口相相似,提供了一個call()方法能夠做爲線程的執行體,可是call()方法要比run()方法更爲強大:# call()方法能夠有返回值;# call()方法能夠聲明拋出異常。那麼使用callable接口是如何獲取返回值的呢?html

1、Callable與Runnable

          既然Callable接口能夠看作是Runnable的「加強版」,那咱們先看看Runnable接口的實現,追根溯源也是搬磚的廣泛素質嘛~java

public interface Runnable {
    public abstract void run();
}

Runnable接口中只包含一個抽象方法run()返回值爲void, 因此在執行完任務以後沒法返回任何結果。接下來咱們能夠看看Callable接口編程

public interface Callable<V> {

    /**
     * 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;
}

這是一個泛型接口,call()函數返回的類型就是傳遞進來的V類型。 那麼該如何使用Callable接口呢?Callable接口不是Runnable接口的子接口,因此Callable對象不能直接做爲Thread的target去運行;並且call方法還有返回值--call()方法並非直接調用。併發

    JDK提供了Future接口來表明call()方法裏的返回值,而且爲Future接口提供了一個實現類FutureTask,該類實現了Future接口,並實現了Runnable接口,其實該類也是Future接口的惟一實現類,因此FutureTask對象能夠做爲Thread的target。Callable通常配合ExecutorService來使用的,在ExecutorService接口中聲明瞭若干個submit方法的重載版本:函數

/**
 * Submits a value-returning task for execution and returns a
 * Future representing the pending results of the task. The
 * Future's <tt>get</tt> method will return the task's result upon
 * successful completion.
 */
<T> Future<T> submit(Callable<T> task);

/**
 * Submits a Runnable task for execution and returns a Future
 * representing that task. The Future's <tt>get</tt> method will
 * return the given result upon successful completion.
 */
<T> Future<T> submit(Runnable task, T result);

/**
 * Submits a Runnable task for execution and returns a Future
 * representing that task. The Future's <tt>get</tt> method will
 * return <tt>null</tt> upon <em>successful</em> completion.
 */
Future<?> submit(Runnable task);

通常狀況下咱們使用第一個submit方法和第三個submit方法,第二個submit方法不多使用 。spa

       這裏提一下ExecutorService的submit與execute方法的區別:ExecutorService的submit與execute方法都能執行任務,但在使用過程,發現其對待run方法拋出的異常處理方式不同。二者執行任務最後都會經過Executor的execute方法來執行,但對於submit,會將runnable物件包裝成FutureTask,其run方法會捕捉被包裝的Runnable Object的run方法拋出的Throwable異常,待submit方法所返回的的Future Object調用get方法時,將執行任務時捕獲的Throwable Object包裝成java.util.concurrent.ExecutionException來拋出。
而對於execute方法,則會直接拋出異常,該異常不能被捕獲,想要在出現異常時作些處理,能夠實現Thread.UncaughtExceptionHandler接口。.net

        當將一個Callable的對象傳遞給ExecutorService的submit方法,則該call方法自動在一個線程上執行,而且會返回執行結果Future對象。一樣,將Runnable的對象傳遞給ExecutorService的submit方法,則該run方法自動在一個線程上執行,而且會返回執行結果Future對象,可是在該Future對象上調用get方法,將返回null。線程

2、Future接口

Future就是對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。code

Future類位於java.util.concurrent包下 :htm

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

在Future接口中聲明瞭5個方法,下面依次解釋每一個方法的做用:

  • cancel方法用來取消任務,若是取消任務成功則返回true,若是取消任務失敗則返回false。參數 mayInterruptIfRunning表示是否容許取消正在執行卻沒有執行完畢的任務,若是設置true,則表示能夠取消正在執行過程當中的任務。若是任務已經完成,則不管mayInterruptIfRunning爲true仍是false,此方法確定返回false,即若是取消已經完成的任務會返 回false;若是任務正在執行,若mayInterruptIfRunning設置爲true,則返回true,若 mayInterruptIfRunning設置爲false,則返回false;若是任務尚未執行,則不管 mayInterruptIfRunning爲true仍是false,確定返回true。
  • isCancelled方法表示任務是否被取消成功,若是在任務正常完成前被取消成功,則返回 true。
  • isDone方法表示任務是否已經完成,若任務完成,則返回true;
  • get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
  • get(long timeout, TimeUnit unit)用來獲取執行結果,若是在指定時間內,還沒獲取到結果,就直接返回null。

  也就是說Future提供了三種功能:

  1)判斷任務是否完成;

  2)可以中斷任務;

  3)可以獲取任務執行結果。

由於Future只是一個接口,因此是沒法直接用來建立對象使用的,所以就有了FutureTask。

3、FutureTask實現類

咱們先來看一下FutureTask的實現:

public class FutureTask<V> implements RunnableFuture<V>

FutureTask類實現了RunnableFuture接口,咱們再來看一下RunnableFuture接口的實現:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

能夠看出RunnableFuture繼承了Runnable接口和Future接口,而FutureTask實現了RunnableFuture接口。因此它既能夠做爲Runnable被線程執行,又能夠做爲Future獲得Callable的返回值

FutureTask提供了2個構造器:

public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}

如何有童鞋對FutureTask具體內容感興趣,能夠看一下這篇文章:FutureTask深刻解析

4、使用示例

1.使用Callable+Future獲取執行結果 :

public class CallableTest implements Callable<String>{
	
	private int id;
	
	public CallableTest(int ThreadId){
		id = ThreadId;
	}
	
	public String call(){
		return "knock knock,who's there ? This is Thead " + id;
	}

	public static void main(String[] args) {
		ExecutorService exec = Executors.newCachedThreadPool();
		ArrayList<Future<String>> results = new ArrayList<Future<String>>();
		for(int i = 0; i < 10; i++){
			results.add(exec.submit(new CallableTest(i)));
		}
		for(Future<String> fs : results){
			try {
				System.out.println(fs.get());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}finally{
				exec.shutdown();
			}
		}
	}

}

執行結果:

knock knock,who's there ? This is Thead 0
knock knock,who's there ? This is Thead 1
knock knock,who's there ? This is Thead 2
knock knock,who's there ? This is Thead 3
knock knock,who's there ? This is Thead 4
knock knock,who's there ? This is Thead 5
knock knock,who's there ? This is Thead 6
knock knock,who's there ? This is Thead 7
knock knock,who's there ? This is Thead 8
knock knock,who's there ? This is Thead 9

其中submit方法會產生Future對象,用Callable返回結果的特定類型進行了參數化。固然,也能夠用isDone()來查詢Future是否已經完成。當任務完成時,能夠調用get()方法來獲取該結果;也能夠直接調用get()方法,該種狀況下,get()將阻塞,直至結果準備就緒。

2.使用Callable+FutureTask獲取執行結果 :

public class CallableTest implements Callable<String>{
	
	private int id;
	
	public CallableTest(int ThreadId){
		id = ThreadId;
	}
	
	public String call(){
		return "knock knock,who's there ? This is Thead " + id;
	}

	public static void main(String[] args) {
		ArrayList<FutureTask<String>> results = new ArrayList<FutureTask<String>>();
		for(int i = 0; i < 10; i++){
			FutureTask<String> ft = new FutureTask<>(new CallableTest(i));
			new Thread(ft).start();
			results.add(ft);
		}
		for(Future<String> fs : results){
			try {
				System.out.println(fs.get());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}
	}

}

執行結果:

knock knock,who's there ? This is Thead 0
knock knock,who's there ? This is Thead 1
knock knock,who's there ? This is Thead 2
knock knock,who's there ? This is Thead 3
knock knock,who's there ? This is Thead 4
knock knock,who's there ? This is Thead 5
knock knock,who's there ? This is Thead 6
knock knock,who's there ? This is Thead 7
knock knock,who's there ? This is Thead 8
knock knock,who's there ? This is Thead 9

能夠看到,使用FutureTask對象來獲取返回結果的時候,該對象能夠做爲Thread中的target對象,因此能夠不使用 ExecutorService  來提交任務(實際上ExecutorService可能要比new Thread().start()方式要慢一點),但運行的結果仍是同樣的。

 

參考文章:

Java併發編程:Callable、Future和FutureTask

java的Future和FutureTask

相關文章
相關標籤/搜索