java線程池學習(三) —— ThreadPoolExecutor

上一篇文章中我們自己寫了一個簡單的線程池。

這一篇文章我們來了解一下java爲我們提供的線程池實現—— ExecutorService接口

它位於jdk的java.util.concurrent包下。

JDK提供了這麼兩個類來實現這個接口:

  • ThreadPoolExecutor
  • ScheduledThreadPoolExecutor

我們這篇文章只介紹一下ThreadPoolExecutor類(ScheduledThreadPoolExecutor類類似,多加入了計劃任務功能。)

我們首先看看怎麼用ThreadPoolExecutor類初始化一個線程池:

//初始化一個線程池
//核心線程數
int  corePoolSize  = 5;
//最大線程數
int  maxPoolSize   = 10;
//空閒線程最大存活時間
long keepAliveTime = 5000;
//任務隊列
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(5);
        
ExecutorService threadPoolExecutor =
        new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                TimeUnit.MILLISECONDS,
                queue
        );

這個類完全實現了一個類似於我們上一篇文章中實現的線程池,它包含以下幾個屬性:

1. corePoolSize 

 核心線程數:即使沒有任何任務過來,線程池裏面也會有保持的最基本線程數。

 2. maximumPoolSize

 最大線程數(即使任務特別多,線程池裏的線程數也不會超過它)

 3. keepAliveTime

 空閒線程最大存活時間

 4. blockingQueue

 任務隊列,用來存放待處理的任務。我們在這個系列的第一篇文章中就介紹過了它。

 可以選擇以下幾個阻塞隊列。

  1. ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。
  2. LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。
  3. SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue。
  4. PriorityBlockingQueue:一個具有優先級得無限阻塞隊列。  

那麼整個類的結構就如下圖所示:

要使用這個線程池的話,可以使用它提供給我們的如下方法:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(...)
  • invokeAll(...)

execute()和submit()可以向這個線程池提交單個任務。他們的區別是:

使用execute提交任務,但是execute方法沒有返回值,所以無法判斷任務知否被線程池執行成功。

使用submit 方法來提交任務,它會返回一個future對象,那麼我們可以通過這個future對象來判斷任務是否執行成功

invokeAll可以直接把一個List類型的任務列表一次性的提交給線程池執行。

==================================================================================

那麼接下來我們就用 ThreadPoolExecutor 來創建一個線程池,改寫一下我們上一篇文章的例子:

public class ProblemCreater {

	public static void main(String[] args) throws Exception {
		//初始化線程池
		//核心線程數
		int  corePoolSize  = 5;
		//最大線程數
		int  maxPoolSize   = 10;
		long keepAliveTime = 5000;
                
		ExecutorService threadPoolExecutor =
		        new ThreadPoolExecutor(
		                corePoolSize,
		                maxPoolSize,
		                keepAliveTime,
		                TimeUnit.MILLISECONDS,
		                new ArrayBlockingQueue<Runnable>(5)
		        );
		
		//生成者不斷產生任務
		for(int i=1;i<10;i++){
			//定義一個新的任務
			Runnable task = new Runnable(){
				public void run(){
					Random random = new Random();
					//隨機一個數字模擬需要解決的時間
					int randomTime = Math.abs(random.nextInt())%20;
					try {
						Thread.sleep(randomTime*1000);
						System.out.println("任務完成,花費時間爲:"+randomTime);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			//將問題插入到線程池任務隊列中
			threadPoolExecutor.execute(task);
			System.out.println("插入新的任務"+i);
		}
	}
}

我們再回頭看看這個 ThreadPoolExecutor 的初始化,我們需要給它傳遞5個參數。核心線程數,最大線程數,線程存活時間,時間單位,還有阻塞隊列的類型。

這個過程還是比較繁瑣的。其實Java幫我們簡化了這個過程,我們可以根據不同的情景,直接用一行代碼創建一個合適的線程池。

實現這個功能的就是 java.util.concurrent.Executors類。我們在下一篇文章中再詳細介紹這個類的用法。


發佈了40 篇原創文章 · 獲贊 32 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章