public interface Future<V> Future 表示異步計算的結果。
Future有個get方法而獲取結果只有在計算完成時獲取,否則會一直阻塞直到任務轉入完成狀態,然後會返回結果或者拋出異常。
Future 主要定義了5個方法:
1)boolean cancel(boolean mayInterruptIfRunning):試圖取消對此任務的執行。如果任務已完成、或已取消,或者由於某些其他原因而無法取消,則此嘗試將失敗。當調用 cancel 時,如果調用成功,而此任務尚未啓動,則此任務將永不運行。如果任務已經啓動,則 mayInterruptIfRunning 參數確定是否應該以試圖停止任務的方式來中斷執行此任務的線程。此方法返回後,對 isDone() 的後續調用將始終返回
true。如果此方法返回 true,則對 isCancelled() 的後續調用將始終返回 true。
2)boolean isCancelled():如果在任務正常完成前將其取消,則返回 true。
3)boolean isDone():如果任務已完成,則返回 true。 可能由於正常終止、異常或取消而完成,在所有這些情況中,此方法都將返回 true。
4)V get()throws InterruptedException,ExecutionException:如有必要,等待計算完成,然後獲取其結果。
5)V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException:如有必要,最多等待爲使計算完成所給定的時間之後,獲取其結果(如果結果可用)。
- public class FutureTask<V> extends Object
- implements Future<V>, Runnable
Executor框架利用FutureTask來完成異步任務,並可以用來進行任何潛在的耗時的計算。一般FutureTask多用於耗時的計算,主線程可以在完成自己的任務後,再去獲取結果。
JDK:
此類提供了對 Future 的基本實現。僅在計算完成時才能檢索結果;如果計算尚未完成,則阻塞 get 方法。一旦計算完成,就不能再重新開始或取消計算。
可使用 FutureTask 包裝 Callable 或 Runnable 對象。因爲 FutureTask 實現了 Runnable,所以可將 FutureTask 提交給 Executor 執行。
- 構造方法摘要
- FutureTask(Callable<V> callable)
- 創建一個 FutureTask,一旦運行就執行給定的 Callable。
- FutureTask(Runnable runnable, V result)
- 創建一個 FutureTask,一旦運行就執行給定的 Runnable,並安排成功完成時 get 返回給定的結果 。
- 參數:
- runnable - 可運行的任務。
- result - 成功完成時要返回的結果。
- 如果不需要特定的結果,則考慮使用下列形式的構造:Future<?> f = new FutureTask<Object>(runnable, null)
Example1:
下面的例子模擬一個會計算賬的過程,主線程已經獲得其他帳戶的總額了,爲了不讓主線程等待 PrivateAccount類的計算結果的返回而啓用新的線程去處理, 並使用 FutureTask對象來監控,這樣,主線程還可以繼續做其他事情, 最後需要計算總額的時候再嘗試去獲得privateAccount 的信息。
- package test;
- import java.util.Random;
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.FutureTask;
- /**
- *
- * @author Administrator
- *
- */
- @SuppressWarnings("all")
- public class FutureTaskDemo {
- public static void main(String[] args) {
- // 初始化一個Callable對象和FutureTask對象
- Callable pAccount = new PrivateAccount();
- FutureTask futureTask = new FutureTask(pAccount);
- // 使用futureTask創建一個線程
- Thread pAccountThread = new Thread(futureTask);
- System.out.println("futureTask線程現在開始啓動,啓動時間爲:" + System.nanoTime());
- pAccountThread.start();
- System.out.println("主線程開始執行其他任務");
- // 從其他賬戶獲取總金額
- int totalMoney = new Random().nextInt(100000);
- System.out.println("現在你在其他賬戶中的總金額爲" + totalMoney);
- System.out.println("等待私有賬戶總金額統計完畢...");
- // 測試後臺的計算線程是否完成,如果未完成則等待
- while (!futureTask.isDone()) {
- try {
- Thread.sleep(500);
- System.out.println("私有賬戶計算未完成繼續等待...");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println("futureTask線程計算完畢,此時時間爲" + System.nanoTime());
- Integer privateAccountMoney = null;
- try {
- privateAccountMoney = (Integer) futureTask.get();
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
- System.out.println("您現在的總金額爲:" + totalMoney + privateAccountMoney.intValue());
- }
- }
- @SuppressWarnings("all")
- class PrivateAccount implements Callable {
- Integer totalMoney;
- @Override
- public Object call() throws Exception {
- Thread.sleep(5000);
- totalMoney = new Integer(new Random().nextInt(10000));
- System.out.println("您當前有" + totalMoney + "在您的私有賬戶中");
- return totalMoney;
- }
- }
運行結果
futureTask線程現在開始啓動,啓動時間爲:3098040622063
主線程開始執行其他任務
現在你在其他賬戶中的總金額爲56983
等待私有賬戶總金額統計完畢...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
私有賬戶計算未完成繼續等待...
您當前有3345在您的私有賬戶中
私有賬戶計算未完成繼續等待...
futureTask線程計算完畢,此時時間爲3103072404138
您現在的總金額爲:569833345
- public class FutureTaskSample {
- static FutureTask<String> future = new FutureTask(new Callable<String>(){
- public String call(){
- return getPageContent();
- }
- });
- public static void main(String[] args) throws InterruptedException, ExecutionException{
- //Start a thread to let this thread to do the time exhausting thing
- new Thread(future).start();
- //Main thread can do own required thing first
- doOwnThing();
- //At the needed time, main thread can get the result
- System.out.println(future.get());
- }
- public static String doOwnThing(){
- return "Do Own Thing";
- }
- public static String getPageContent(){
- return "testPageContent and provide that the operation is a time exhausted thing...";
- }
- }
編寫多線程程序是爲了實現多任務的併發執行,從而能夠更好地與用戶交互。一般有三種方法,Thread,Runnable,Callable.
Runnable和Callable的區別是,
(1)Callable規定的方法是call(),Runnable規定的方法是run().
(2)Callable的任務執行後可返回值,而Runnable的任務是不能返回值得
(3)call方法可以拋出異常,run方法不可以
(4)運行Callable任務可以拿到一個Future對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future對象可以瞭解任務執行情況,可取消任務的執行,還可獲取執行結果。
1、通過實現Runnable接口來創建Thread線程:
步驟1:創建實現Runnable接口的類:
class SomeRunnable implements Runnable
{
public void run()
{
//do something here
}
}
步驟2:創建一個類對象:
Runnable oneRunnable = new SomeRunnable();
步驟3:由Runnable創建一個Thread對象:
Thread oneThread = new Thread(oneRunnable);
步驟4:啓動線程:
oneThread.start();
至此,一個線程就創建完成了。
註釋:線程的執行流程很簡單,當執行代碼oneThread.start();時,就會執行oneRunnable對象中的void run();方法,
該方法執行完成後,線程就消亡了。
2、與方法1類似,通過實現Callable接口來創建Thread線程:其中,Callable接口(也只有一個方法)定義如下:
public interface Callable<V>
{
V call() throws Exception;
}
步驟1:創建實現Callable接口的類SomeCallable<Integer>(略);
步驟2:創建一個類對象:
Callable<Integer> oneCallable = new SomeCallable<Integer>();
步驟3:由Callable<Integer>創建一個FutureTask<Integer>對象:
FutureTask<Integer> oneTask = new FutureTask<Integer>(oneCallable);
註釋:FutureTask<Integer>是一個包裝器,它通過接受Callable<Integer>來創建,它同時實現了Future和Runnable接口。
步驟4:由FutureTask<Integer>創建一個Thread對象:
Thread oneThread = new Thread(oneTask);
步驟5:啓動線程:
oneThread.start();
至此,一個線程就創建完成了。
3、通過繼承Thread類來創建一個線程:
步驟1:定義一個繼承Thread類的子類:
class SomeThead extends Thraad
{
public void run()
{
//do something here
}
}
步驟2:構造子類的一個對象:
SomeThread oneThread = new SomeThread();
步驟3:啓動線程:
oneThread.start();
至此,一個線程就創建完成了。
註釋:這種創建線程的方法不夠好,主要是因爲其涉及運行機制問題,影響程序性能。
4、通過線程池來創建線程:
步驟1:創建線程池:
ExecutorService pool = Executors.newCachedThreadPool();
步驟2:通過Runnable對象或Callable對象將任務提交給ExecutorService對象:
Future<Integer> submit(Callable<Integer> task);
註釋:Future是一個接口,它的定義如下:
public interface Future<T>
{
V get() throws ...;
V get(long timeout, TimeUnit unit) throws ...;
void cancle(boolean mayInterrupt);
boolean isCancelled();
boolean isDone();
}
至此,一個線程就創建完成了。
註釋:線程池需調用shutdown();方法來關閉線程。
5、通過事件分配線程直接使用程序中的原有線程:
使用方法:
直接調用EventQueue類的靜態方法invokeLater():
EventQueue.invokeLater(oneRunnable);
註釋:調用EventQueue.invokeLater(oneRunnable);會直接執行oneRunnable對象中的run()方法。