編寫多線程程序一般有三種方法,Thread,Runnable,Callable.
Runnable和Callable的區別是:
(1)Callable規定的方法是call(),Runnable規定的方法是run()。其中Runnable可以提交給Thread來包裝下,直接啓動一個線程來執行,而Callable則一般都是提交給ExecuteService來執行。
(2)Callable的任務執行後可返回值,而Runnable的任務是不能返回值得
(3)call方法可以拋出異常,run方法不可以
(4)運行Callable任務可以拿到一個Future對象,c表示異步計算的結果。
Future 提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。計算完成後只能使用 get 方法來獲取結果,如果線程沒有執行完,Future.get()方法可能會阻塞當前線程的執行;如果線程出現異常,Future.get()會throws InterruptedException或者ExecutionException;如果線程已經取消,會跑出CancellationException。取消由cancel 方法來執行。isDone確定任務是正常完成還是被取消了。一旦計算完成,就不能再取消計算。如果爲了可取消性而使用 Future 但又不提供可用的結果,則可以聲明Future<?> 形式類型,並返回 null 作爲底層任務的結果。
/**
- 通過簡單的測試程序來試驗Runnable、Callable通過Executor來調度的時候與Future的關係
*/
package com.hadoop.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class RunnableAndCallable2Future {
public static void main(String[] args) {
// 創建一個執行任務的服務
ExecutorService executor = Executors.newFixedThreadPool(3);
try {
//1.Runnable通過Future返回結果爲空
//創建一個Runnable,來調度,等待任務執行完畢,取得返回結果
Future<?> runnable1 = executor.submit(new Runnable() {
@Override
public void run() {
System.out.println("runnable1 running.");
}
});
System.out.println("Runnable1:" + runnable1.get());
// 2.Callable通過Future能返回結果
//提交併執行任務,任務啓動時返回了一個 Future對象,
// 如果想得到任務執行的結果或者是異常可對這個Future對象進行操作
Future<String> future1 = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "result=task1";
}
});
// 獲得任務的結果,如果調用get方法,當前線程會等待任務執行完畢後才往下執行
System.out.println("task1: " + future1.get());
//3. 對Callable調用cancel可以對對該任務進行中斷
//提交併執行任務,任務啓動時返回了一個 Future對象,
// 如果想得到任務執行的結果或者是異常可對這個Future對象進行操作
Future<String> future2 = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
try {
while (true) {
System.out.println("task2 running.");
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Interrupted task2.");
}
return "task2=false";
}
});
// 等待5秒後,再停止第二個任務。因爲第二個任務進行的是無限循環
Thread.sleep(10);
System.out.println("task2 cancel: " + future2.cancel(true));
// 4.用Callable時拋出異常則Future什麼也取不到了
// 獲取第三個任務的輸出,因爲執行第三個任務會引起異常
// 所以下面的語句將引起異常的拋出
Future<String> future3 = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
throw new Exception("task3 throw exception!");
}
});
System.out.println("task3: " + future3.get());
} catch (Exception e) {
System.out.println(e.toString());
}
// 停止任務執行服務
executor.shutdownNow();
}
}
執行結果如下:
runnable1 running.
Runnable1:null
task1: result=task1
task2 running.
task2 cancel: true
Interrupted task2.
java.util.concurrent.ExecutionException: java.lang.Exception: Bad flag value!
FutureTask則是一個RunnableFuture,即實現了Runnbale又實現了Futrue這兩個接口,另外它還可以包裝Runnable和Callable,所以一般來講是一個符合體了,它可以通過Thread包裝來直接執行,也可以提交給ExecuteService來執行,並且還可以通過v get()返回執行結果,在線程體沒有執行完成的時候,主線程一直阻塞等待,執行完則直接返回結果。
public class FutureTaskTest {
/**
* @param args
*/
public static void main(String[] args) {
Callable<String> task = new Callable<String>() {
public String call() {
System.out.println("Sleep start.");
try {
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Sleep end.");
return "time=" + System.currentTimeMillis();
}
};
//直接使用Thread的方式執行
FutureTask<String> ft = new FutureTask<String>(task);
Thread t = new Thread(ft);
t.start();
try {
System.out.println("waiting execute result");
System.out.println("result = " + ft.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//使用Executors來執行
System.out.println("=========");
FutureTask<String> ft2 = new FutureTask<String>(task);
Executors.newSingleThreadExecutor().submit(ft2);
try {
System.out.println("waiting execute result");
System.out.println("result = " + ft2.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
執行結果如下:
waiting execute result
Sleep start.
Sleep end.
result = time=1370844662537
waiting execute result
Sleep start.
Sleep end.
result = time=1370844672542