Java併發--任務執行

這篇文章就主要討論討論Java併發中的任務執行,來作爲我整理的地一篇文章吧。 文中難免有錯,如果發現問題,可以即時站內或者回帖交流。
  OK,言歸正傳
 
首先來看一下,任務的定義:

 所謂的任務,就是抽象,離散的工作單位。你可以簡單理解爲代碼級別的 (Runnable接口)

 大多數併發應用程序都是圍繞着任務進行管理的.

 我們來看一小段代碼:

Java代碼 複製代碼
  1. package com.ivan.concurrent.charpter6;   
  2.   
  3. import java.net.ServerSocket;   
  4. import java.net.Socket;   
  5.   
  6. /**  
  7.  * 順序化的Web Server.  
  8.  * @author root  
  9.  * OS:Ubuntu 9.04  
  10.  * Date:2010-6-19  
  11.  */  
  12. public class SingleThreadWebServer {   
  13.     public static void main(String[] args) throws Exception {   
  14.         ServerSocket server=new ServerSocket(8080);   
  15.         while(true){   
  16.             Socket socket=server.accept();   
  17.             handleRequest(socket);   
  18.         }   
  19.     }   
  20.   
  21.     private static void handleRequest(Socket socket) {   
  22.         /**  
  23.          * 做相關的處理……, 比如請求運算與I/O  
  24.          *   這將會導致出現阻塞,  會延遲當前請求的處理,  
  25.          *   而且會產生非常嚴重的後果,比如: 假死。  
  26.          *    那樣會極度考驗用戶的耐心,知道他忍無可忍的關閉瀏覽器。  
  27.          *   同時,單線程在等待IO操作時,CPU處於閒置狀態,這樣也降低了資源的利用率   
  28.          *     
  29.          *  這樣的服務器,缺乏良好的吞吐量和快速的響應性。  
  30.          */  
  31.     }   
  32. }  
package com.ivan.concurrent.charpter6;

import java.net.ServerSocket;
import java.net.Socket;

/**
 * 順序化的Web Server.
 * @author root
 * OS:Ubuntu 9.04
 * Date:2010-6-19
 */
public class SingleThreadWebServer {
	public static void main(String[] args) throws Exception {
		ServerSocket server=new ServerSocket(8080);
		while(true){
			Socket socket=server.accept();
			handleRequest(socket);
		}
	}

	private static void handleRequest(Socket socket) {
		/**
		 * 做相關的處理……, 比如請求運算與I/O
		 *   這將會導致出現阻塞,  會延遲當前請求的處理,
		 *   而且會產生非常嚴重的後果,比如: 假死。
		 *    那樣會極度考驗用戶的耐心,知道他忍無可忍的關閉瀏覽器。
		 *   同時,單線程在等待IO操作時,CPU處於閒置狀態,這樣也降低了資源的利用率 
		 *   
		 *  這樣的服務器,缺乏良好的吞吐量和快速的響應性。
		 */
	}
}

 




上面的代碼是順序地執行任務,主線程在不斷接受連接與處理請求之間交替運行。
一個Web請求會做相關的處理……, 比如請求運算與I/O
 這將會導致出現阻塞,  會延遲當前請求的處理,
 而且會產生非常嚴重的後果,比如: 假死。
 那樣會極度考驗用戶的耐心,知道他忍無可忍的關閉瀏覽器。
 同時,單線程在等待IO操作時,CPU處於閒置狀態,這樣也降低了資源的利用率
 這樣的服務器,缺乏良好的吞吐量和快速的響應性。

所以,基於上面代碼的基礎上,我們需要給他作些小許的改進:

Java代碼 複製代碼
  1. package com.ivan.concurrent.charpter6;   
  2.   
  3. import java.net.ServerSocket;   
  4. import java.net.Socket;   
  5.   
  6. public class ThreadPerTaskWebServer {   
  7.     public static void main(String[] args) throws Exception {   
  8.         ServerSocket server=new ServerSocket(80);   
  9.         while(true){   
  10.             final Socket socket=server.accept();   
  11.             new Thread(new Runnable(){   
  12.                 public void run() {   
  13.                     handleRequest(socket);   
  14.                 }   
  15.             }).start();   
  16.         }   
  17.     }   
  18.   
  19.     protected static void handleRequest(Socket socket) {   
  20.         /**  
  21.          *相比較而言,這樣的處理方式有良好的改進:  
  22.          * 1.執行人物的負載已經脫離主線程,讓主循環能更加迅速的重新開始等待下一個連接。提高了響應性  
  23.          * 2.併發處理任務,多個請求可以同時得到處理,提高了吞吐性  
  24.          * 3.任務處理代碼必須要是線程安全的。防止出現併發性數據共享問題。   
  25.          *   
  26.          * 這個程序可能在開發階段運行良好,一旦部署,就可能出現致命的錯誤,  
  27.          * 我們接着來分析:  
  28.          */  
  29.     }   
  30. }  
package com.ivan.concurrent.charpter6;

import java.net.ServerSocket;
import java.net.Socket;

public class ThreadPerTaskWebServer {
	public static void main(String[] args) throws Exception {
		ServerSocket server=new ServerSocket(80);
		while(true){
			final Socket socket=server.accept();
			new Thread(new Runnable(){
				public void run() {
					handleRequest(socket);
				}
			}).start();
		}
	}

	protected static void handleRequest(Socket socket) {
		/**
		 *相比較而言,這樣的處理方式有良好的改進:
		 * 1.執行人物的負載已經脫離主線程,讓主循環能更加迅速的重新開始等待下一個連接。提高了響應性
		 * 2.併發處理任務,多個請求可以同時得到處理,提高了吞吐性
		 * 3.任務處理代碼必須要是線程安全的。防止出現併發性數據共享問題。 
		 * 
		 * 這個程序可能在開發階段運行良好,一旦部署,就可能出現致命的錯誤,
		 * 我們接着來分析:
		 */
	}
}

 


    相比較而言,這樣的處理方式有良好的改進:
    1.執行人物的負載已經脫離主線程,讓主循環能更加迅速的重新開始等待下一個連接。提高了響應性
     2.併發處理任務,多個請求可以同時得到處理,提高了吞吐性
     3.任務處理代碼必須要是線程安全的。防止出現併發性數據共享問題。
    
 這個程序可能在開發階段運行良好,一旦部署,就可能出現致命的錯誤,
 我們接着來分析:

 我們看到,上面的代碼中,是爲每個請求的到來,創建一個新的線程來處理, 那麼這樣就會有以下的問題出現:


無限創建線程的缺點:
1.線程生命週期的開銷
1.1.線程的創建與關閉並非是免費的,實際的開銷根據不同的OS有不同的處理.但是線程的創建的確需要時間,帶來處理請求的延遲.一般的Web Server的請求是很頻繁的,爲每個請求創建一個線程,無非要耗費大量的資源.
2.資源消耗量
2.1. 活動的線程會消耗資源,尤其是內存.如果可運行的線程數多於可用的處理器數,線程將會空閒。大量的空閒線程佔用更多的內存,給垃圾回收器帶來壓力,而且,線程在競爭CPU的同時,也會帶來許多其他的性能開銷。所以,建議在有足夠多的線程讓CPU忙碌時,不要再創建多餘的線程.
3.應用的穩定性
3.1. 應該限制創建線程的數量,限制的數目根據不同的平臺而定,同時也受到JVM的啓動參數,Thread的構造函數中棧大小等因素的影響. 如果打破了這個限制,你很可能會得到一個OutOfMemoryError. 在一定範圍內增加線程可以提高系統的吞吐量,但是一旦超過這個範圍,再創建線程只會拖垮你的系統。甚至可能會導致應用程序的崩潰.
  
我們的解決辦法:
    使用線程池,當然,你完全沒有必要自己寫一個線程池的實現(好吧,或許你跟我一樣,也希望能從重複創造輪子中,找到自己想要了解的東西),你可以利用 Executor框架來幫你處理,java.util.concurrent提供了一個靈活的線程池實現。在新的java類庫當中,任務執行的首要抽象不是Thread,而是Executor.
    Executor僅僅是一個簡單的接口,但是它很強大,包括用於異步任務的執行,支持不同類型的任務執行策略,爲任務提交和任務執行之間的解藕,提供了標準的方式等等, 我們後續再重點討論。
    Executor基於 生產者-消費者模式。提交任務的是生產者,執行任務的是消費者。 也就是說, 採用Executor框架實現 生產者-消費者模式,十分簡單。

Java代碼 複製代碼
  1. package com.ivan.concurrent.charpter6;   
  2.   
  3. import java.net.ServerSocket;   
  4. import java.net.Socket;   
  5. import java.util.concurrent.Executor;   
  6. import java.util.concurrent.Executors;   
  7.   
  8. public class TaskExecutionWebServer{   
  9.     private static final int NTHREADS=100;   
  10.     //使用線程池來避免 爲每個請求創建一個線程。   
  11.     private static final Executor threadPool=Executors.newFixedThreadPool(NTHREADS);   
  12.        
  13.     public static void main(String[] args) throws Exception {   
  14.         ServerSocket server=new ServerSocket(8011);   
  15.         while(true){   
  16.             final Socket socket=server.accept();   
  17.             threadPool.execute(new Runnable(){   
  18.                 public void run() {   
  19.                     handleRequest(socket);   
  20.                 }   
  21.             });   
  22.         }   
  23.     }   
  24.   
  25.     protected static void handleRequest(Socket socket) {   
  26.         /**  
  27.          *  
  28.          */  
  29.         System.out.println(Thread.currentThread().getId());   
  30.         try {   
  31.             Thread.sleep(5000);   
  32.         } catch (InterruptedException e) {   
  33.             e.printStackTrace();   
  34.         }   
  35.     }   
  36.        
  37.        
  38. }  
package com.ivan.concurrent.charpter6;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class TaskExecutionWebServer{
	private static final int NTHREADS=100;
	//使用線程池來避免 爲每個請求創建一個線程。
	private static final Executor threadPool=Executors.newFixedThreadPool(NTHREADS);
	
	public static void main(String[] args) throws Exception {
		ServerSocket server=new ServerSocket(8011);
		while(true){
			final Socket socket=server.accept();
			threadPool.execute(new Runnable(){
				public void run() {
					handleRequest(socket);
				}
			});
		}
	}

	protected static void handleRequest(Socket socket) {
		/**
		 *
		 */
		System.out.println(Thread.currentThread().getId());
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	
}

 

線程池:
    線程池管理着一個工作者線程的同構池,線程池是與工作隊列緊密綁定的。工作隊列的作用就是持有所有等待執行的任務, 工作者隊列只需要從工作隊列中獲取到下一個任務,執行,然後回來等待下一個線程。
    Java類庫中提供了以下幾種線程池:
1.newFixedThreadPool :創建定長的線程池,每當提交一個任務就創建一個線程,直到達到池的最大長度。
2.newCachedThreadPool:創建一個可緩存的線程池,如果當前線程池的長度超過了處理的需要,它可以靈活的收回空閒線程,當需求增加時,它可以靈活添加新的線程,而並不對池的長度做任何限制
3.newSingleThreadExecutor:創建單線程化的executor,它只創建唯一的工作者線程來執行任務,如果這個線程異常結束,會有另外一個線程來取代它.它會保證任務按照任務隊列規定的順序來執行。
4.NewScheduledThreadPool:創建一個定長的線程池,而且支持定時的,以及週期性的任務執行,類似Timer.

Executor的生命週期:
    它的創建已經說了,我們來看看它如何關閉, Executor 是爲了執行任務而創建線程,而JVM通常會在所有非後臺線程退出後才退出,如果它無法正確的關閉,則會影響到JVM的結束。
    這裏需要提一下,在我們瞭解如何關閉Executor的一些疑惑,  由於Executor是異步執行任務,那麼這些任務的狀態不是立即可見的,換句話說,在任務時間裏,這些執行的任務中,有的可能已經完成,有的還可能在運行,其他的還可能在隊列裏面等待。 爲了解決這些問題, Java引入了另外一個接口,它擴展了Executor,並增加一些生命週期的管理方法: ExecutorService.


ExecutorService表示生命週期有三種狀態:  運行,關閉,終止。
    關閉和終止? 怎麼看上去是一個意思, 這裏我們先擱置着,留着後續來討論。
    
ExecutorService最初創建後的初始狀態就是運行狀態;
    shutdown與shutdownNow方法,都是ExecutorService的關閉方法,區別在於:
    shutdown:
        會啓動一個平穩的關閉過程, 停止接受新任務,同時等待已經提交的任務完成(包括尚未開始執行的任務)
    shutdownNow:
        會啓動一個強制關閉的過程:嘗試取消所有運行中的任務和排在隊列中尚未開始的任務。

    一旦所有任務全完成後,ExecutorService會轉到終止狀態, awaitTermination可以用來等待ExecutorService到達終止狀態,也可以輪詢isTerminated判斷ExecutorService是否已經終止。

Java代碼 複製代碼
  1. package com.ivan.concurrent.charpter6;   
  2.   
  3. import java.io.IOException;   
  4. import java.net.ServerSocket;   
  5. import java.net.Socket;   
  6. import java.util.concurrent.ExecutorService;   
  7. import java.util.concurrent.Executors;   
  8. import java.util.concurrent.RejectedExecutionException;   
  9.   
  10. /**  
  11.  * 線程池的生命週期是如何管理的?  
  12.  * @author root  
  13.  * OS:Ubuntu 9.04  
  14.  * Date:2010-6-19  
  15.  */  
  16. public class LifeCycleWebServer {   
  17.     private static final int NTHREADS=100;   
  18.     private static final ExecutorService exec=Executors.newFixedThreadPool(NTHREADS);   
  19.        
  20.     public void start() throws IOException{   
  21.         ServerSocket server=new ServerSocket(8011);   
  22.         while(exec.isShutdown()){   
  23.             try {   
  24.                 final Socket socket=server.accept();   
  25.                 exec.execute(new Runnable(){   
  26.                     public void run() {   
  27.                         handleRequest(socket);   
  28.                     }   
  29.                 });   
  30.             } catch (RejectedExecutionException e) {   
  31.                 if(!exec.isShutdown()){   
  32.                     //log.error(...)   
  33.                 }   
  34.             }   
  35.         }   
  36.     }   
  37.        
  38.        
  39.     protected void handleRequest(Socket socket) {   
  40.         Request req=readRequest(socket);   
  41.         if(isShutDown(req)){   
  42.             stop();   
  43.         }else{   
  44.             dispatchRequest(req);   
  45.         }   
  46.     }   
  47.   
  48.     public void stop(){   
  49.         exec.shutdown();   
  50.     }   
  51.        
  52.        
  53.     //~ Mock Object And Function..   
  54.     private static class Request{   
  55.            
  56.     }   
  57.        
  58.     private Request readRequest(Socket socket) {   
  59.         // TODO Auto-generated method stub   
  60.         return null;   
  61.     }   
  62.   
  63.   
  64.     private boolean isShutDown(Request req) {   
  65.         // TODO Auto-generated method stub   
  66.         return false;   
  67.     }   
  68.   
  69.   
  70.     private void dispatchRequest(Request req) {   
  71.         // TODO Auto-generated method stub   
  72.            
  73.     }   
  74.        
  75. }  
package com.ivan.concurrent.charpter6;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;

/**
 * 線程池的生命週期是如何管理的?
 * @author root
 * OS:Ubuntu 9.04
 * Date:2010-6-19
 */
public class LifeCycleWebServer {
	private static final int NTHREADS=100;
	private static final ExecutorService exec=Executors.newFixedThreadPool(NTHREADS);
	
	public void start() throws IOException{
		ServerSocket server=new ServerSocket(8011);
		while(exec.isShutdown()){
			try {
				final Socket socket=server.accept();
				exec.execute(new Runnable(){
					public void run() {
						handleRequest(socket);
					}
				});
			} catch (RejectedExecutionException e) {
				if(!exec.isShutdown()){
					//log.error(...)
				}
			}
		}
	}
	
	
	protected void handleRequest(Socket socket) {
		Request req=readRequest(socket);
		if(isShutDown(req)){
			stop();
		}else{
			dispatchRequest(req);
		}
	}

	public void stop(){
		exec.shutdown();
	}
	
	
	//~ Mock Object And Function..
	private static class Request{
		
	}
	
	private Request readRequest(Socket socket) {
		// TODO Auto-generated method stub
		return null;
	}


	private boolean isShutDown(Request req) {
		// TODO Auto-generated method stub
		return false;
	}


	private void dispatchRequest(Request req) {
		// TODO Auto-generated method stub
		
	}
	
}

 


OK,瞭解了線程池的使用,這裏有必要介紹介紹執行策略,

執行策略:
    簡單來說,就是任務執行的”What,When,Where,How”,包括:
1.任務在什麼線程中執行(what)
2.任務以什麼順序執行(fifo,lifo,優先級)?
3.可以有多少個任務併發執行?(how many)
4.可以有多少個任務進入等待執行隊列
5.系統過載時,需要放棄一個任務,該挑選哪一個? 如何通知應用程序知道?

 


另外,java類庫中還提供有一種特別的任務,----可攜帶結果的任務:
    Callable 和 Future
    Runnable 作爲任務的基本表達形式只是個相當有限的抽象; 它的侷限在於,不能返回一個值或者拋出受檢查的異常。
    通常,很多任務都會引起嚴重的計算延遲,比如執行數據庫查詢,從網絡下載資源,進行復雜的計算。對於這樣的任務,Callable是更佳的抽象: 它在主進入點,等待返回值,併爲可能拋出的異常預先作準備。
    Runnable與Callable描述的都是首相的計算型任務,這些任務通常都是有限的。,任務的所生命週期分爲4個階段: 創建、提交、開始和完成。
    Future描述了任務的生命週期,並提供了相關的方法來獲取任務的結果、取消任務以及檢驗任務是否已經完成或者被取消。
    Future的get方法取決於任務的狀態, 如果任務已經完成,get會立即返回或者拋出異常,如果任務沒有完成,get會阻塞直到它的完成。
    
    創建Future的方法有很多, ExecutorService的submit會返回一個Future,你可以將一個Callable或者Runnable提交給executor,然後得到一個Future,用它來重新獲得任務執行的結果,或者取消任務。
    你也可以顯示的爲給定的Callable和Runnable實例化一個FutureTask.

    
OK, 前面介紹了很多關於併發的理論知識,下面我們來看看,如果尋找可強化的併發性。

首先,我們從一個例子開始, 開始之前,簡單介紹一下這個例子所要表達的事情:
    它的來源是瀏覽器程序中渲染頁面的那部分功能, 首先獲取HTML,並將它渲染到圖像緩存裏。爲了簡單起見,我們假設HTML只有文本標籤。 OK, 開始吧。

    首先,如果按照一般的處理方式,我們會這樣做:
1.遇到文本標籤,將它渲染到圖像緩存中
2.當遇到的是一個圖片標籤,我們通過網絡獲取它,再將它放到緩存裏面。
    
    很明顯,這是最簡單的方式, 它很容易實現,但是,問題在於,你這樣做,是在考驗用戶的耐心,結果就是他會對着屏幕丟一句 ****.然後毫不猶豫的關掉瀏覽器.

    另外一種方法:
     它先渲染文本,併爲圖像預留出佔位符;在完成第一趟文本處理後,程序返回開始,並下載圖像,將它們繪製到佔位符上去。 但是這樣的問題也很明顯, 需要最少2次的文檔處理, 其性能與效率稍有提升,但是還不足解決用戶希望快速瀏覽頁面的需求。

    爲了使我們的渲染器具有更高的併發性,我們需要做的第一步就是, 將渲染過程分爲兩部分: 一個用來渲染文本,一個用來下載所有圖像。(一個受限於CPU,另外一個受限於IO, 即使在單CPU系統上,效率的提升也很明顯。)
    Callable與Future可以用來表達所有協同工作的任務之間的交互。我們來看代碼:

Java代碼 複製代碼
  1. package com.ivan.concurrent.charpter6;   
  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 FutureRenderer {   
  12.     private static final int NTHREADS=100;   
  13.     private static final ExecutorService exec=Executors.newFixedThreadPool(NTHREADS);   
  14.        
  15.     void renderPage(CharSequence source){   
  16.         final List<ImageInfo> imageinfos=scanForImageInfo(source);   
  17.         Callable<List<ImageData>> task=   
  18.                 new Callable<List<ImageData>>(){   
  19.                     public List<ImageData> call() throws Exception {   
  20.                         List<ImageData> result=new ArrayList<ImageData>();   
  21.                         for(ImageInfo imageinfo:imageinfos){   
  22.                             result.add(imageinfo.downloadImage());   
  23.                         }   
  24.                         return result;   
  25.                     }   
  26.                
  27.         };   
  28.            
  29.            
  30.         Future<List<ImageData>> future=exec.submit(task);   
  31.         //保證渲染文本與下載圖像數據併發執行。   
  32.         renderText(source);   
  33.         try {   
  34.             /**  
  35.              * 到達需要所有圖像的時間點時,主任務會等待future.get調用的結果,  
  36.              *  幸運的話,我們請求的同時,下載已經完成,即使沒有,下載也已經預先開始了。  
  37.              *    
  38.              *  這裏還有一定的侷限性, 用戶可能不希望等待所有圖片下載完成後纔可以看見,  
  39.              *   他希望下載完成一張圖片後,就可以立即看到。 …… 這裏還待優化。  
  40.              */  
  41.             List<ImageData> imageData=future.get();   
  42.                
  43.             for(ImageData data:imageData){   
  44.                 reanderImage(data);   
  45.             }   
  46.         } catch (InterruptedException e) {   
  47.             Thread.currentThread().interrupt();   
  48.             future.cancel(true);//取消任務   
  49.         }catch(ExecutionException e){   
  50.             e.printStackTrace();   
  51.                
  52.         }   
  53.     }   
  54.   
  55.     private void renderText(CharSequence source) {   
  56.         // TODO Auto-generated method stub   
  57.            
  58.     }   
  59.   
  60.     private void reanderImage(ImageData data) {   
  61.         // TODO Auto-generated method stub   
  62.            
  63.     }   
  64.   
  65.     private List<ImageInfo> scanForImageInfo(CharSequence source) {   
  66.         // TODO Auto-generated method stub   
  67.         return null;   
  68.     }   
  69. }  
package com.ivan.concurrent.charpter6;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureRenderer {
	private static final int NTHREADS=100;
	private static final ExecutorService exec=Executors.newFixedThreadPool(NTHREADS);
	
	void renderPage(CharSequence source){
		final List<ImageInfo> imageinfos=scanForImageInfo(source);
		Callable<List<ImageData>> task=
				new Callable<List<ImageData>>(){
					public List<ImageData> call() throws Exception {
						List<ImageData> result=new ArrayList<ImageData>();
						for(ImageInfo imageinfo:imageinfos){
							result.add(imageinfo.downloadImage());
						}
						return result;
					}
			
		};
		
		
		Future<List<ImageData>> future=exec.submit(task);
		//保證渲染文本與下載圖像數據併發執行。
		renderText(source);
		try {
			/**
			 * 到達需要所有圖像的時間點時,主任務會等待future.get調用的結果,
			 *  幸運的話,我們請求的同時,下載已經完成,即使沒有,下載也已經預先開始了。
			 *  
			 *  這裏還有一定的侷限性, 用戶可能不希望等待所有圖片下載完成後纔可以看見,
			 *   他希望下載完成一張圖片後,就可以立即看到。 …… 這裏還待優化。
			 */
			List<ImageData> imageData=future.get();
			
			for(ImageData data:imageData){
				reanderImage(data);
			}
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
			future.cancel(true);//取消任務
		}catch(ExecutionException e){
			e.printStackTrace();
			
		}
	}

	private void renderText(CharSequence source) {
		// TODO Auto-generated method stub
		
	}

	private void reanderImage(ImageData data) {
		// TODO Auto-generated method stub
		
	}

	private List<ImageInfo> scanForImageInfo(CharSequence source) {
		// TODO Auto-generated method stub
		return null;
	}
}

 



CompletionService: 當executorService遇到BlockingQueue
    CompletionService整合了Executor和BlockingQueue的功能,你可以將Callable任務提交給它去執行,然後使用類似於隊列中的take和poll方法,在結果完成可用時,獲得這個結果,像一個打包的Future.
  我們利用它來爲我們的渲染器需要優化的地方做些處理,代碼如下:

Java代碼 複製代碼
  1. package com.ivan.concurrent.charpter6;   
  2.   
  3. import java.util.List;   
  4. import java.util.concurrent.Callable;   
  5. import java.util.concurrent.CompletionService;   
  6. import java.util.concurrent.ExecutionException;   
  7. import java.util.concurrent.ExecutorCompletionService;   
  8. import java.util.concurrent.ExecutorService;   
  9. import java.util.concurrent.Executors;   
  10. import java.util.concurrent.Future;   
  11.   
  12. public class FutureRenderer2 {   
  13.     private static final int NTHREADS=100;   
  14.     private static final ExecutorService exec=Executors.newFixedThreadPool(NTHREADS);   
  15.        
  16.     void renderPage(CharSequence source){   
  17.         final List<ImageInfo> imageinfos=scanForImageInfo(source);   
  18.            
  19.         CompletionService<ImageData> completionService=new ExecutorCompletionService<ImageData>(exec);   
  20.            
  21.         for(final ImageInfo imageinfo:imageinfos){   
  22.             completionService.submit(new Callable<ImageData>(){   
  23.                 public ImageData call() throws Exception {   
  24.                     //提高性能點一: 將順序的下載,變成併發的下載,縮短下載時間   
  25.                     return imageinfo.downloadImage();   
  26.                 }   
  27.             });   
  28.         }   
  29.         renderText(source);   
  30.         try {   
  31.             for(int i=0;i<imageinfos.size();i++){   
  32.                 Future<ImageData> f=completionService.take();   
  33.                 //提高性能點二: 下載完成一張圖片後,立刻渲染到頁面。   
  34.                 ImageData imagedata=f.get();   
  35.                 reanderImage(imagedata);   
  36.             }   
  37.         } catch (InterruptedException e) {   
  38.             Thread.currentThread().interrupt();   
  39.         }catch(ExecutionException e){   
  40.             e.printStackTrace();   
  41.                
  42.         }   
  43.     }   
  44.   
  45.     private void renderText(CharSequence source) {   
  46.         // TODO Auto-generated method stub   
  47.            
  48.     }   
  49.   
  50.     private void reanderImage(ImageData data) {   
  51.         // TODO Auto-generated method stub   
  52.            
  53.     }   
  54.   
  55.     private List<ImageInfo> scanForImageInfo(CharSequence source) {   
  56.         // TODO Auto-generated method stub   
  57.         return null;   
  58.     }   
  59. }  
package com.ivan.concurrent.charpter6;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureRenderer2 {
	private static final int NTHREADS=100;
	private static final ExecutorService exec=Executors.newFixedThreadPool(NTHREADS);
	
	void renderPage(CharSequence source){
		final List<ImageInfo> imageinfos=scanForImageInfo(source);
		
		CompletionService<ImageData> completionService=new ExecutorCompletionService<ImageData>(exec);
		
		for(final ImageInfo imageinfo:imageinfos){
			completionService.submit(new Callable<ImageData>(){
				public ImageData call() throws Exception {
					//提高性能點一: 將順序的下載,變成併發的下載,縮短下載時間
					return imageinfo.downloadImage();
				}
			});
		}
		renderText(source);
		try {
			for(int i=0;i<imageinfos.size();i++){
				Future<ImageData> f=completionService.take();
				//提高性能點二: 下載完成一張圖片後,立刻渲染到頁面。
				ImageData imagedata=f.get();
				reanderImage(imagedata);
			}
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
		}catch(ExecutionException e){
			e.printStackTrace();
			
		}
	}

	private void renderText(CharSequence source) {
		// TODO Auto-generated method stub
		
	}

	private void reanderImage(ImageData data) {
		// TODO Auto-generated method stub
		
	}

	private List<ImageInfo> scanForImageInfo(CharSequence source) {
		// TODO Auto-generated method stub
		return null;
	}
}

 

  OK, 文章先寫道這裏, 本文參考於Java併發大師Brian Goetz的 《Java併發編程與實踐》第6章, 文中有自己的一些理解,也可以算是讀書筆記。 

轉自:http://www.javaeye.com/topic/694591

 多線程的學習索引:http://bbs.misonsoft.com/thread-1088-1-1.html 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章