1、概述異步
到目前爲止,咱們已經可以聲明並使一個線程任務運行起來了。可是遇到一個問題:如今定義的任務都沒有任何返回值,那麼加入咱們但願一個任務運行結束後告訴我一個結果,該結果表名任務執行成功或失敗,此時該怎麼辦呢?ide
答案是使用Callable。以前定義的任務都直接實現了Runnable,該接口的run方法並沒有返回值。而Callable的call方法能夠根據你傳入的泛型參數返回對應類型的數據。函數
2、實現this
1.實現Callable接口,定義可返回結果的線程任務spa
public class TaskCallable implements Callable<String>{ private int id; public TaskCallable(int id){ this.id = id; } @Override public String call() throws Exception { return "result of taskWithResult "+id; } }
注意,泛型參數String表示的是該任務執行以後返回結果的類型。.net
2.將該任務交給線程執行者executor,讓他來代理執行這些線程線程
ExecutorService exec = Executors.newCachedThreadPool();//工頭 ArrayList<Future<String>> results = new ArrayList<Future<String>>();// for(int i = 0 ; i < 10 ;i++){ results.add(exec.submit(new TaskCallable(i)));//submit返回一個Future,表明了即將要返回的結果 }
注意,此時須要使用executor的submit方法來調用Callable的call。代理
該方法將返回一個Future接口的對象,它的泛型參數表明了call方法要返回的參數類型。code
3.Future類型對象
簡單的瞭解了下Future類型:按照名字判斷該類型對象表明了線程執行完成後的結果,因此叫Future。那麼在獲取該類型存放的線程運行結果時,可能該線程並未運行完畢,因此稱其爲「未來的結果」。
從線程中返回數據的兩種方法
一、經過類變量和類方法返回數據
二、經過回調函數返回數據
三、實現 Callable<V>接口,其中 V 表明 返回值類型
1、經過變量和方法返回數據
先看以下一段代碼
public class MyThread extends Thread
{
private String value1;
private String value2;
public void run()
{
value1 = "value1";
value2 = "value2";
}
public static void main(String[] args)
{
MyThread t1 = new MyThread();
t1.start();
System.out.println(t1.value1);
System.out.println(t1.value2);
}
}
輸出結果 :
null
null
1
2
上面的運行結果很不正常,在run方法中已經對value1和value2進行賦值,可是返回倒是null。發生這種狀況的緣由是: 在調用strat()方法後就當即輸出 value1 和 value2 的值,而這裏的run 方法 尚未指定到value1 和 value2 賦值語句。
若是要避免這種狀況,就須要等run方法執行完成後才輸出 vaue1 和value2 代碼。能夠考慮使用sleep 將主線程進行延遲,可是有一個問題,你不知道須要延遲多久才能知道 value2 或 value1 有值 !!! 咱們能夠這樣作,以下代碼
public class MyThread extends Thread
{
private String value1;
private String value2;
public void run()
{
value1 = "value1";
value2 = "value2";
}
public static void main(String[] args) throws InterruptedException
{
MyThread t1 = new MyThread();
t1.start();
while(t1.value1 == null || t1.value1 == null)
{
sleep(100);
}
System.out.println(t1.value1);
System.out.println(t1.value2);
}
}
輸出結果:
value1
value2
1
2
以上方法雖然幫助咱們解決了問題,可是Java的線程模型爲咱們提供了更好的解決方案,就是使用join()方法,join()方法的功能 就是使線程 從異步執行 變成同步執行 。當線程變成同步執行後,就跟普通方法中獲得返回數據沒什麼區別了。
public static void main(String[] args) throws InterruptedException
{
MyThread t1 = new MyThread();
t1.start();
t1.join();
System.out.println(t1.value1);
System.out.println(t1.value2);
}
2、經過回調函數返回數據
跟 經過回調方法向線程傳遞 數據的 思路一致
3、實現 Callable 接口
一、定義任意類並實現 Callable 接口,並實現 方法 V call() throws Exception。其中,v 皆是表明返回值類型
二、在 V call() throws Exception 方法裏 定義 方法體,並 return 一個返回值
三、建立線程池,建立任務對象,線程池經過submit(任務對象) 執行任務,並返回一個 Future<V> 對象
四、經過Future對象.get() 方法獲取返回值
五、關閉線程池,釋放資源
public class MyThread implements Callable<String>
{
private String value;
public MyThread(String value)
{
this.value = value;
}
public String call() throws Exception
{
System.out.println(Thread.currentThread().getName());//pool-1-thread-1
return "線程返回值是:"+this.value;
}
public static void main(String[] args)
{
//建立一個線程池對象
ExecutorService pool = Executors.newCachedThreadPool();
//建立一個有返回值的任務
MyThread task = new MyThread("Java");
//執行任務並獲取Future對象
Future<String> future = pool.submit(task);
//從 Future 對象 獲取任務返回值
while(true)
{
//能夠用isDone()方法來查詢Future是否已經完成,任務完成後,能夠調用get()方法來獲取結果
//注意: 若是不加判斷直接調用get方法,此時若是線程未完成,get將阻塞,直至結果準備就緒
if(future.isDone())
{
try
{
String returnValue = future.get().toString();
System.out.println("線程返回值:"+returnValue);
}catch (Exception e){
e.printStackTrace();
}
//關閉線程池
pool.shutdown();
//跳出循環
break;
}
}
}