多線程之Future模式


詳細參見葛一名老師的《Java程序性能優化》

Futrue模式:對於多線程,如果線程A要等待線程B的結果,那么線程A沒必要等待B,直到B有結果,可以先拿到一個未來的Future,等B有結果是再取真實的結果。

 在多線程中經常舉的一個例子就是:網絡圖片的下載,剛開始是通過模糊的圖片來代替最后的圖片,等下載圖片的線程下載完圖片后在替換。而在這個過程中可以做一些其他的事情。

  首先客戶端向服務器請求RealSubject,但是這個資源的創建是非常耗時的,怎么辦呢?這種情況下,首先返回Client一個FutureSubject,以滿足客戶端的需求,於此同時呢,Future會通過另外一個Thread 去構造一個真正的資源,資源准備完畢之后,在給future一個通知。如果客戶端急於獲取這個真正的資源,那么就會阻塞客戶端的其他所有線程,等待資源准備完畢。

  公共數據接口,FutureData和RealData都要實現。

1 public interface Data {  
2     public abstract String getContent();  
3 }  

  FutureData,當有線程想要獲取RealData的時候,程序會被阻塞。等到RealData被注入才會使用getReal()方法。

 1 package com.volshell.future;
 2 
 3 public class FutureData implements Data {
 4 
 5     protected RealData realData = null;
 6     protected boolean isReady = false;
 7 
 8     @Override
 9     public synchronized String getResult() {
10         // TODO Auto-generated method stub
11         while (!isReady) {
12             try {
13                 wait();
14             } catch (Exception e) {
15                 // TODO: handle exception
16             }
17         }
18         return realData.result;
19     }
20 
21     public synchronized void setRealData(RealData realData) {
22         if (isReady)
23             return;
24         this.realData = realData;
25         isReady = true;
26         notifyAll();
27     }
28 }

  真實數據RealData

 1 package com.volshell.future;
 2 
 3 public class RealData implements Data {
 4     protected String result;
 5 
 6     public RealData(String para) {
 7         StringBuffer sb = new StringBuffer();
 8         for (int i = 0; i < 10; i++) {
 9             sb.append(para);
10             try {
11                 Thread.sleep(100);
12             } catch (Exception e) {
13                 // TODO: handle exception
14             }
15             result = sb.toString();
16         }
17     }
18 
19     @Override
20     public String getResult() {
21         // TODO Auto-generated method stub
22         return result;
23     }
24 
25 }

  客戶端程序:

 1 package com.volshell.future;
 2 
 3 public class Client {
 4     public Data request(final String request){
 5         final FutureData future = new FutureData();
 6         new Thread(){
 7                 public void run() {
 8                     
 9                     RealData reaData = new RealData(request);
10                     future.setRealData(reaData);
11                 };
12         }.start();
13         return future;
14     }
15 }

  調用者:

 1 package com.volshell.future;
 2 
 3 public class Main {
 4     public static void main(String[] args) {
 5         Client client = new Client();
 6         Data data = client.request("name");
 7         System.out.println("請求完畢!!");
 8         try {
 9             Thread.sleep(2000);
10         } catch (Exception e) {
11             // TODO: handle exception
12         }
13         System.out.println("獲取的數據:" +data.getResult());
14     }
15 }

  調用者請求資源,client.request("name"); 完成對數據的准備

  當要獲取資源的時候,data.getResult() ,如果資源沒有准備好isReady = false;那么就會阻塞該線程。直到資源獲取然后該線程被喚醒。

  今天又重新了解了future模式。

 1 package com.volshell.future2;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.concurrent.Callable;
 6 import java.util.concurrent.ExecutionException;
 7 import java.util.concurrent.ExecutorService;
 8 import java.util.concurrent.Executors;
 9 import java.util.concurrent.Future;
10 
11 public class FutureTest2 {
12     private static class Task implements Callable<String> {
13         @Override
14         public String call() throws Exception {
15             // 模擬真實事務的處理過程,這個過程是非常耗時的。
16             Thread.sleep(5000);
17             return "call return ";
18         }
19     }
20 
21     public static void main(String[] args) throws InterruptedException,
22             ExecutionException {
23         List<Future<String>> futures = new ArrayList<Future<String>>();
24         ExecutorService executorService = Executors.newCachedThreadPool();
25 
26         System.out.println("已經提交資源申請");
27         for (int i = 0; i < 10; i++) {
28             futures.add(executorService.submit(new Task()));
29         }
30 
31         for (Future<String> future : futures) {
32             // 判斷資源是不是已經准備完畢,准備完畢直接獲取。
33             if (!future.isDone()) {
34                 System.out.println("資源還沒有准備好");
35             }
36             System.out.println(future.get());
37         }
38         executorService.shutdown();
39     }
40 }

其中的核心就是Callable中的call方法,這個和Runnable中的run 非常類似。

Runnable和Callable都是接口
不同之處:
1.Callable可以返回一個類型V,而Runnable不可以
2.Callable能夠拋出checked exception,而Runnable不可以
3.Runnable是自從java1.1就有了,而Callable是1.5之后才加上去的
4.Callable和Runnable都可以應用於executors。而Thread類只支持Runnable.
上面只是簡單的不同,其實這兩個接口在用起來差別還是很大的。Callable與executors聯合在一起,在任務完成時可立刻獲得一個更新了的Future。而Runable卻要自己處理

 

  Future接口,一般都是取回Callable執行的狀態用的。其中的主要方法:

  • cancel,取消Callable的執行,當Callable還沒有完成時
  • get,獲得Callable的返回值
  • isCanceled,判斷是否取消了
  • isDone,判斷是否完成

 

用Executor來構建線程池,應該要做的事:

1).調用Executors類中的靜態方法newCachedThreadPool(必要時創建新 線程,空閑線程會被保留60秒)或newFixedThreadPool(包含固定數量的線程池)等,返回的是一個實現了ExecutorService 接口的ThreadPoolExecutor類或者是一個實現了ScheduledExecutorServiece接口的類對象。

2).調用submit提交Runnable或Callable對象。

3).如果想要取消一個任務,或如果提交Callable對象,那就要保存好返回的Future對象。

4).當不再提交任何任務時,調用shutdown方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM