深入淺出多線程(3)-Future異步模式以及在JDK1.5Concurrent包中的實現
接深入淺出多線程(2)在多線程交互的中,經常有一個線程需要得到另個一線程的計算結果,我們常用的是Future異步模式來加以解決。
什么是Future模式呢?Future 顧名思義,在金融行業叫期權,市場上有看跌期權和看漲期權,你可以在現在(比如九月份)購買年底(十二月)的石油,假如你買的是看漲期權,那么如果石油真的漲了,你也可以在十二月份依照九月份商定的價格購買。扯遠了,Future就是你可以拿到未來的結果。對于多線程,如果線程A要等待線程B的結果,那么線程A沒必要等待B,直到B有結果,可以先拿到一個未來的Future,等B有結果是再取真實的結果。其實這個模式用的很多,比如瀏覽器下載圖片的時候,剛開始是不是通過模糊的圖片來代替最后的圖片,等下載圖片的線程下載完圖片后在替換。如圖所示:

GenerateResultThread開始進行計算了!
過來2s了,看看有結果嗎?
還沒有完成呢! 繼續干自己活吧!
過來4s了,看看有結果嗎?
完成了!
Result:ResultByGenerateResultThread
注意: 通過JDK標準的Future后,沒有必要增加額外的Object來只有Result,更加簡單明了,同時FutureTask還提供了Cancel的功能,我們持有FutureTask引用后可以Cancel該線程。通過get()取值是,如果結果還沒有返回,將會阻塞Main線程。
看看,當 new Thread(new FutureTask(new Callable())).start 時:
看圖:
G
get 方法取result值,FutureTask 提供Timeout 功能,如果超時,拋出異常。

什么是Future模式呢?Future 顧名思義,在金融行業叫期權,市場上有看跌期權和看漲期權,你可以在現在(比如九月份)購買年底(十二月)的石油,假如你買的是看漲期權,那么如果石油真的漲了,你也可以在十二月份依照九月份商定的價格購買。扯遠了,Future就是你可以拿到未來的結果。對于多線程,如果線程A要等待線程B的結果,那么線程A沒必要等待B,直到B有結果,可以先拿到一個未來的Future,等B有結果是再取真實的結果。其實這個模式用的很多,比如瀏覽器下載圖片的時候,剛開始是不是通過模糊的圖片來代替最后的圖片,等下載圖片的線程下載完圖片后在替換。如圖所示:

- 在沒有JDK1.5提供的Concurrent之前,我們通過自定義一個結果類,負責結果持有。
package vincent.blogjava.net;
public class FutureResult {
private String result;
private boolean isFinish =false;
public String getResult() {
return result;
}
public synchronized void setResult(String result) {
this.result = result;
this.isFinish = true;
}
public synchronized boolean isFinish() {
return isFinish;
}
}
存儲結果值和是否完成的Flag。public class FutureResult {
private String result;
private boolean isFinish =false;
public String getResult() {
return result;
}
public synchronized void setResult(String result) {
this.result = result;
this.isFinish = true;
}
public synchronized boolean isFinish() {
return isFinish;
}
}
package vincent.blogjava.net;
public class GenerateResultThread extends Thread{
FutureResult fr ;
public GenerateResultThread(FutureResult fr ){
this.fr = fr;
}
public void run(){
//模仿大量耗時計算后(5s)返回結果。
try {
System.out.println("GenerateResultThread開始進行計算了!");
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fr.setResult("ResultByGenerateResultThread");
}
}
計算具體業務邏輯并放回結果的線程。public class GenerateResultThread extends Thread{
FutureResult fr ;
public GenerateResultThread(FutureResult fr ){
this.fr = fr;
}
public void run(){
//模仿大量耗時計算后(5s)返回結果。
try {
System.out.println("GenerateResultThread開始進行計算了!");
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fr.setResult("ResultByGenerateResultThread");
}
}
package vincent.blogjava.net;
public class Main {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
FutureResult fr = new FutureResult();
new GenerateResultThread(fr).start();
//main線程無需等待,不會被阻塞。
//模仿 干自己的活 2s。
Thread.sleep(2000);
// 估計算完了吧 取取試試。
System.out.println("過來2s了,看看有結果嗎?");
if(!fr.isFinish()){System.out.println("還沒有完成呢! 繼續干自己活吧!");}
//模仿 干自己的活 4s。
Thread.sleep(4000);
System.out.println("過來4s了,看看有結果嗎?");
if(fr.isFinish()){
System.out.println("完成了!");
System.out.println("Result:"+fr.getResult());
}
}
}
Main方法需要GenerateResultThread線程計算的結果,通過這種模式,main線程不需要阻塞。結果如下:public class Main {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
FutureResult fr = new FutureResult();
new GenerateResultThread(fr).start();
//main線程無需等待,不會被阻塞。
//模仿 干自己的活 2s。
Thread.sleep(2000);
// 估計算完了吧 取取試試。
System.out.println("過來2s了,看看有結果嗎?");
if(!fr.isFinish()){System.out.println("還沒有完成呢! 繼續干自己活吧!");}
//模仿 干自己的活 4s。
Thread.sleep(4000);
System.out.println("過來4s了,看看有結果嗎?");
if(fr.isFinish()){
System.out.println("完成了!");
System.out.println("Result:"+fr.getResult());
}
}
}
GenerateResultThread開始進行計算了!
過來2s了,看看有結果嗎?
還沒有完成呢! 繼續干自己活吧!
過來4s了,看看有結果嗎?
完成了!
Result:ResultByGenerateResultThread
- 在JDK1.5 Concurrent 中,提供了這種Callable的機制。我們只要實現Callable接口中的Call方法,Call方法是可以返回任意類型的結果的。如下:
package vincent.blogjava.net;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ConcurrentImpl {
public static void main(String[] args) throws InterruptedException, Exception {
FutureTask fr = new FutureTask(new Returnresult());
new Thread(fr).start();
//main線程無需等待,不會被阻塞。
//模仿 干自己的活 2s。
Thread.sleep(2000);
// 估計算完了吧 取取試試。
System.out.println("過來2s了,看看有結果嗎?");
if(!fr.isDone()){System.out.println("還沒有完成呢! 繼續干自己活吧!");}
//模仿 干自己的活 4s。
Thread.sleep(4000);
System.out.println("過來4s了,看看有結果嗎?");
if(fr.isDone()){
System.out.println("完成了!");
System.out.println("Result:"+fr.get());
}
}
}
class Returnresult implements Callable{
@Override
public Object call() throws Exception {
//模仿大量耗時計算后(5s)返回結果。
System.out.println("GenerateResultThread開始進行計算了!");
Thread.sleep(11000);
return "ResultByGenerateResultThread";
}
}
Returnresult 實現了Callable接口,在Call方法中實現業務邏輯,并返回結果。在Main方法里面,初始化FutureTask 并將該Task作為Runnable加入Thread后,啟動線程。得到跟剛才相同的效果。import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ConcurrentImpl {
public static void main(String[] args) throws InterruptedException, Exception {
FutureTask fr = new FutureTask(new Returnresult());
new Thread(fr).start();
//main線程無需等待,不會被阻塞。
//模仿 干自己的活 2s。
Thread.sleep(2000);
// 估計算完了吧 取取試試。
System.out.println("過來2s了,看看有結果嗎?");
if(!fr.isDone()){System.out.println("還沒有完成呢! 繼續干自己活吧!");}
//模仿 干自己的活 4s。
Thread.sleep(4000);
System.out.println("過來4s了,看看有結果嗎?");
if(fr.isDone()){
System.out.println("完成了!");
System.out.println("Result:"+fr.get());
}
}
}
class Returnresult implements Callable{
@Override
public Object call() throws Exception {
//模仿大量耗時計算后(5s)返回結果。
System.out.println("GenerateResultThread開始進行計算了!");
Thread.sleep(11000);
return "ResultByGenerateResultThread";
}
}
注意: 通過JDK標準的Future后,沒有必要增加額外的Object來只有Result,更加簡單明了,同時FutureTask還提供了Cancel的功能,我們持有FutureTask引用后可以Cancel該線程。通過get()取值是,如果結果還沒有返回,將會阻塞Main線程。
- 其實JDK 實現Future模式的秘密就在FutureTask類里:
看看,當 new Thread(new FutureTask(new Callable())).start 時:
看圖:

get 方法取result值,FutureTask 提供Timeout 功能,如果超時,拋出異常。

posted on 2008-09-03 00:57 advincenting 閱讀(2637) 評論(4) 編輯 收藏