Executor框架使用Runnable 作為其基本的任務表示形式。Runnable是一種有局限性的抽象,然后可以寫入日志,或者共享的數據結構,但是他不能返回一個值。
許多任務實際上都是存在延遲計算的:執行數據庫查詢,從網絡上獲取資源,或者某個復雜耗時的計算。對於這種任務,Callable是一個更好的抽象,他能返回一個值,並可能拋出一個異常。Future表示一個任務的周期,並提供了相應的方法來判斷是否已經完成或者取消,以及獲取任務的結果和取消任務。
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; }
public interface Future<V> { /** * Attempts to cancel execution of this task. This attempt will * fail if the task has already completed, has already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when <tt>cancel</tt> is called, * this task should never run. If the task has already started, * then the <tt>mayInterruptIfRunning</tt> parameter determines * whether the thread executing this task should be interrupted in * an attempt to stop the task. * * <p>After this method returns, subsequent calls to {@link #isDone} will * always return <tt>true</tt>. Subsequent calls to {@link #isCancelled} * will always return <tt>true</tt> if this method returned <tt>true</tt>. * * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete * @return <tt>false</tt> if the task could not be cancelled, * typically because it has already completed normally; * <tt>true</tt> otherwise */ boolean cancel(boolean mayInterruptIfRunning); /** * Returns <tt>true</tt> if this task was cancelled before it completed * normally. * * @return <tt>true</tt> if this task was cancelled before it completed */ boolean isCancelled(); /** * Returns <tt>true</tt> if this task completed. * * Completion may be due to normal termination, an exception, or * cancellation -- in all of these cases, this method will return * <tt>true</tt>. * * @return <tt>true</tt> if this task completed */ boolean isDone(); /** * Waits if necessary for the computation to complete, and then * retrieves its result. * * @return the computed result * @throws CancellationException if the computation was cancelled * @throws ExecutionException if the computation threw an * exception * @throws InterruptedException if the current thread was interrupted * while waiting */ V get() throws InterruptedException, ExecutionException; /** * Waits if necessary for at most the given time for the computation * to complete, and then retrieves its result, if available. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @return the computed result * @throws CancellationException if the computation was cancelled * @throws ExecutionException if the computation threw an * exception * @throws InterruptedException if the current thread was interrupted * while waiting * @throws TimeoutException if the wait timed out */ V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
可以通過多種方法來創建一個Future來描述任務。ExecutorService中的submit方法接受一個Runnable或者Callable,然后返回一個Future來獲得任務的執行結果或者取消任務。
/** * 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. * * <p> * If you would like to immediately block waiting * for a task, you can use constructions of the form * <tt>result = exec.submit(aCallable).get();</tt> * * <p> Note: The {@link Executors} class includes a set of methods * that can convert some other common closure-like objects, * for example, {@link java.security.PrivilegedAction} to * {@link Callable} form so they can be submitted. * * @param task the task to submit * @return a Future representing pending completion of the task * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if the task is null */ <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. * * @param task the task to submit * @param result the result to return * @return a Future representing pending completion of the task * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if the task is null */ <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. * * @param task the task to submit * @return a Future representing pending completion of the task * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if the task is null */ Future<?> submit(Runnable task);
另外ThreadPoolExecutor中的newTaskFor(Callable<T> task) 可以返回一個FutureTask。
假設我們通過一個方法從遠程獲取一些計算結果,假設方法是 List getDataFromRemote(),如果采用同步的方法,代碼大概是 List data = getDataFromRemote(),我們將一直等待getDataFromRemote返回,然后才能繼續后面的工作,這個函數是從遠程獲取計算結果的,如果需要很長時間,后面的代碼又和這個數據沒有什么關系的話,阻塞在那里就會浪費很多時間。我們有什么辦法可以改進呢???
能夠想到的辦法是調用函數后,立即返回,然后繼續執行,等需要用數據的時候,再取或者等待這個數據。具體實現有兩種方式,一個是用Future,另一個是回調。
Future<List> future = getDataFromRemoteByFuture(); //do something.... List data = future.get();
可以看到我們返回的是一個Future對象,然后接着自己的處理后面通過future.get()來獲得我們想要的值。也就是說在執行getDataFromRemoteByFuture的時候,就已經啟動了對遠程計算結果的獲取,同時自己的線程還繼續執行不阻塞。知道獲取時候再拿數據就可以。看一下getDataFromRemoteByFuture的實現:
private Future<List> getDataFromRemoteByFuture() { return threadPool.submit(new Callable<List>() { @Override public List call() throws Exception { return getDataFromRemote(); } }); }
我們在這個方法中調用getDataFromRemote方法,並且用到了線程池。把任務加入線程池之后,理解返回Future對象。Future的get方法,還可以傳入一個超時參數,用來設置等待時間,不會一直等下去。
也可以利用FutureTask來獲取結果:
FutureTask<List> futureTask = new FutureTask<List>(new Callable<List>() { @Override public List call() throws Exception { return getDataFromRemote(); } }); threadPool.submit(futureTask); futureTask.get();
FutureTask是一個具體的實現類,ThreadPoolExecutor的submit方法返回的就是一個Future的實現,這個實現就是FutureTask的一個具體實例,FutureTask幫助實現了具體的任務執行,以及和Future接口中的get方法的關聯。FutureTask除了幫助ThreadPool很好的實現了對加入線程池任務的Future支持外,也為我們提供了很大的便利,使得我們自己也可以實現支持Future的任務調度。