1. 程式人生 > >【Java多執行緒】Executor框架的詳解

【Java多執行緒】Executor框架的詳解

在Java中,使用執行緒來非同步執行任務。Java執行緒的建立與銷燬需要一定的開銷,如果我們為每一個任務建立一個新執行緒來執行,這些執行緒的建立與銷燬將消耗大量的計算資源。同時,為每一個任務建立一個新執行緒來執行,這種策略可能會使處於高負荷狀態的應用最終崩潰。

Java執行緒既是工作單元,也是執行單元。從JDK1.5開始,把工作單元與執行機制分離開來。工作單元包括Runnable 和 Callable,而執行機制由Executor框架提供。

Executor框架簡介

Executor框架的兩級排程模型

在HotSpot VM的執行緒模型中,Java執行緒被一對一對映為本地作業系統執行緒。Java執行緒啟動時會建立一個本地作業系統執行緒;當Java執行緒終止時,這個作業系統執行緒也會被回收。作業系統會呼叫所有執行緒並將他們分配給可用的CPU。

可以將此種模式分為兩層,在上層,Java多執行緒程式通常把應用程式分解為若干任務,然後使用使用者級的排程器(Executor框架)講這些任務對映為固定數量的執行緒;在底層,作業系統核心將這些執行緒對映到硬體處理器上。

兩級排程模型的示意圖:

這裡寫圖片描述

從圖中可以看出,該框架用來控制應用程式的上層排程(下層排程由作業系統核心控制,不受應用程式的控制)。

Executor框架的結構和成員

Executor框架的結構

1. 任務

包括被執行任務需要實現的介面:Runnable介面和Callable介面

2. 任務的執行

包括任務執行機制的核心介面Executor,以及繼承自Executor的ExecutorService介面。

Executor框架有兩個關鍵類實現了ExecutorService介面:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor

3. 非同步計算的結果

包括Future和實現Future介面的FutureTask類。

Executor框架的類與介面

示意圖

  • Executor是一個介面,他是Executor框架的基礎,它將任務的提交與任務的執行分離。
  • ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後執行命令,或者定期執行命令。ScheduledThreadPoolExecutor 比 Timer 更靈活,功能更強大。
  • Future介面和它的實現FutureTask類,代表非同步計算的結果。
  • Runnable和Callable介面的實現類,都可以被ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 執行。

Executor框架的使用

 先來看個圖:

  1. 主執行緒首先要建立實現 Runnable介面或者Callable介面的任務物件。工具類Executors可以把一個Runnable物件封裝為一個Callable物件

    1

    2

    3

    Executors.callable(Runnale task);

    Executors.callable(Runnable task, Object resule);

  2. 然後可以把Runnable物件直接交給ExecutorService執行

    1

    2

    3

    ExecutorServicel.execute(Runnable command);

    或者也可以把Runnable物件或Callable物件提交給ExecutorService執行

    ExecutorService.submit(Runnable task);

    如果執行ExecutorService.submit(...),ExecutorService將返回一個實現Future介面的物件(到目前為止的JDK中,返回的是FutureTask物件)。由於FutureTask實現了Runnable介面,我們也可以建立FutureTask類,然後直接交給ExecutorService執行。  

  3. 最後,主執行緒可以執行FutureTask.get()方法來等待任務執行完成。主執行緒也可以執行FutureTask.cancel(boolean mayInterruptIfRunning)來取消此任務的執行。

 ThreadPoolExecutor詳解

 ThreadPoolExecutor的元件構成

  • corePool:核心執行緒池的大小
  • maximumPool:最大執行緒池的大小
  • BlockingQueue:用來暫時儲存任務的工作佇列
  • RejectedExecutionHandler:當ThreadPoolExecutor已經關閉或ThreadPoolExecutor已經飽和時(達到了最大執行緒池的大小且工作佇列已滿),execute()方法將要呼叫的Handler。

 1. FixedThreadPool

建立固定長度的執行緒池,每次提交任務建立一個執行緒,直到達到執行緒池的最大數量,執行緒池的大小不再變化。

這個執行緒池可以建立固定執行緒數的執行緒池。特點就是可以重用固定數量執行緒的執行緒池。它的構造原始碼如下:

1

2

3

4

5

public static ExecutorService newFixedThreadPool(int nThreads) { 

return new ThreadPoolExecutor(nThreads, nThreads, 0L,

TimeUnit.MILLISECONDS, 

new LinkedBlockingQueue<Runnable>()); 

  • FixedThreadPool的corePoolSize和maxiumPoolSize都被設定為建立FixedThreadPool時指定的引數nThreads。
  • 0L則表示當執行緒池中的執行緒數量操作核心執行緒的數量時,多餘的執行緒將被立即停止
  • 最後一個引數表示FixedThreadPool使用了無界佇列LinkedBlockingQueue作為執行緒池的做工佇列,由於是無界的,當執行緒池的執行緒數達到corePoolSize後,新任務將在無界佇列中等待,因此執行緒池的執行緒數量不會超過corePoolSize,同時maxiumPoolSize也就變成了一個無效的引數,並且執行中的執行緒池並不會拒絕任務。

FixedThreadPool執行圖如下

執行過程如下:

1.如果當前工作中的執行緒數量少於corePool的數量,就建立新的執行緒來執行任務。

2.當執行緒池的工作中的執行緒數量達到了corePool,則將任務加入LinkedBlockingQueue。

3.執行緒執行完1中的任務後會從佇列中去任務。

注意LinkedBlockingQueue是無界佇列,所以可以一直新增新任務到執行緒池。

2. SingleThreadExecutor  

SingleThreadExecutor是使用單個worker執行緒的Executor。特點是使用單個工作執行緒執行任務。它的構造原始碼如下:

1

2

3

4

5

6

public static ExecutorService newSingleThreadExecutor() {

return new FinalizableDelegatedExecutorService

(new ThreadPoolExecutor(1, 1,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>()));

}

SingleThreadExecutor的corePoolSize和maxiumPoolSize都被設定1。

其他引數均與FixedThreadPool相同,其執行圖如下:

 

執行過程如下:

1.如果當前工作中的執行緒數量少於corePool的數量,就建立一個新的執行緒來執行任務。

2.當執行緒池的工作中的執行緒數量達到了corePool,則將任務加入LinkedBlockingQueue。

3.執行緒執行完1中的任務後會從佇列中去任務。

注意:由於線上程池中只有一個工作執行緒,所以任務可以按照新增順序執行。

 3. CachedThreadPool

 CachedThreadPool是一個”無限“容量的執行緒池,它會根據需要建立新執行緒。特點是可以根據需要來建立新的執行緒執行任務,沒有特定的corePool。下面是它的構造方法:

1

2

3

4

5

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue<Runnable>());

}

CachedThreadPool的corePoolSize被設定為0,即corePool為空;maximumPoolSize被設定為Integer.MAX_VALUE,即maximum是無界的。這裡keepAliveTime設定為60秒,意味著空閒的執行緒最多可以等待任務60秒,否則將被回收。

CachedThreadPool使用沒有容量的SynchronousQueue作為主執行緒池的工作佇列,它是一個沒有容量的阻塞佇列。每個插入操作必須等待另一個執行緒的對應移除操作。這意味著,如果主執行緒提交任務的速度高於執行緒池中處理任務的速度時,CachedThreadPool會不斷建立新執行緒。極端情況下,CachedThreadPool會因為建立過多執行緒而耗盡CPU資源。其執行圖如下:

執行過程如下:

1.首先執行SynchronousQueue.offer(Runnable task)。如果在當前的執行緒池中有空閒的執行緒正在執行SynchronousQueue.poll(),那麼主執行緒執行的offer操作與空閒執行緒執行的poll操作配對成功,主執行緒把任務交給空閒執行緒執行。,execute()方法執行成功,否則執行步驟2

2.當執行緒池為空(初始maximumPool為空)或沒有空閒執行緒時,配對失敗,將沒有執行緒執行SynchronousQueue.poll操作。這種情況下,執行緒池會建立一個新的執行緒執行任務。

3.在建立完新的執行緒以後,將會執行poll操作。當步驟2的執行緒執行完成後,將等待60秒,如果此時主執行緒提交了一個新任務,那麼這個空閒執行緒將執行新任務,否則被回收。因此長時間不提交任務的CachedThreadPool不會佔用系統資源。

SynchronousQueue是一個不儲存元素阻塞佇列,每次要進行offer操作時必須等待poll操作,否則不能繼續新增元素。

相關推薦

Java執行Executor框架

在Java中,使用執行緒來非同步執行任務。Java執行緒的建立與銷燬需要一定的開銷,如果我們為每一個任務建立一個新執行緒來執行,這些執行緒的建立與銷燬將消耗大量的計算資源。同時,為每一個任務建立一個新執行緒來執行,這種策略可能會使處於高負荷狀態的應用最終崩潰。 Java執行

Java執行執行狀態、執行池狀態

執行緒狀態:     執行緒共包括以下5種狀態。1. 新建狀態(New)  執行緒物件被建立後,就進入了新建狀態。例如,Thread thread = new Thread()。2. 就緒狀態(Runnable)  也被稱為“可執行狀態”。執行緒物件被建立後,

Java執行Executor框架

在前面的這篇文章中介紹了執行緒池的相關知識,現在我們來看一下跟執行緒池相關的框架--Executor。 一.什麼是Executor 1.Executor框架的兩級排程模型 在HotSpot VM的執行緒模型中,Java執行緒(java.lang.Thread)被一對一對映為本地作業系統執

Java執行ReentrantReadWriteLock

概述   ReentrantReadWriteLock是Lock的另一種實現方式,ReentrantLock是一個排他鎖,同一時間只允許一個執行緒訪問,而ReentrantReadWriteLock允許多個讀執行緒同時訪問,但不允許寫執行緒和讀執行緒、寫執行緒和寫執行緒同時訪問。相對於排他鎖,提高了併發性。

JAVA執行如何解決一個生產者與消費者問題

                             如何解決一個生產者與消費者問題 生產者與消費者問題是多執行緒同步的一個經典問題。生產者和消費者同時使用一塊緩衝區,生產者生產商品放入緩衝區,消費者從緩衝區中取出商品。我們需要保證的是,當緩衝區滿時,生產者不可生產商品

輸出三角形 Java 執行

題目: 利用多執行緒輸出兩個三角形,要求控制兩個執行緒的發生,判斷第一個執行緒是否結束。 並且在第一個執行緒沒結束時,使用 sleep 方法或者 join 方法。 1、sleep 方法。 public class Main { public static

各視窗共同賣票 Java 執行

現實中也存在這樣的情況: 模擬一個火車站的售票系統,假如當日從A地發往B地的火車票只有10張,且允許所有視窗賣這10張票,每一個視窗也相當於一個執行緒。 1、Runnable介面實現 public class Main { public static void

各視窗單獨賣票 Java 執行

多視窗賣票,需求如下: 假設一個影院有三個售票口,分別用於向兒童、成人和老人售票。 影院為每個視窗放有10張電影票,分別是兒童票、成人票和老人票。 三個視窗需要同時賣票, 而現在只有一個售票員,這個售票員就相當於一個CPU,三個視窗就相當於三個執行緒。 1 通過擴充套件T

java執行守護執行執行停止、volatile的深入瞭解

文章目錄 執行緒的優雅停止 守護執行緒 volatile關鍵字 執行緒的優雅停止       在多執行緒的操作之中如果要啟動多執

Java執行執行池的工作原理(下)

接著上篇文章,我接下來繼續介紹執行緒池的工作原理,如果你還沒有看上篇,我建議最好瀏覽一下:執行緒池的工作原理詳解(上) Executors 工具類 1.定義 Executors是java執行緒池的工廠類,通過它可以快速初始化一個符合業務需求的執行緒池。

java執行執行常用操作方法總結

文章目錄 執行緒的命名與獲取 執行緒休眠 執行緒中斷 執行緒強制執行 執行緒禮讓 執行緒優先順序 多執行緒的主要操作方法都在Thread類中定義了。 執行緒的

Java執行Executor框架和手寫簡易的執行

目錄 Java多執行緒之一執行緒及其基本使用 Java多執行緒之二(Synchronized) Java多執行緒之三volatile與等待通知機制示例 執行緒池 什麼是執行緒池 執行緒池一種執行緒使用模式,執行緒池會維護多個執行緒,等待著分配可併發執行的任務,當有任務需要執行緒執行時

Java執行ThreadLocal

Synchronized是為了讓多執行緒進行資料共享,而ThreadLocal為了讓多執行緒進行資料隔離。 ThreadLocal為變數在每個執行緒中都建立了一個副本,那麼每個執行緒可以訪問自己內部

Java執行共享變數&同步-非同步容器&執行區域性變數

共享變數 (Volatile Atomic) volatile:當多個執行緒訪問一個成員變數的時候,需要這個變數在多個執行緒中可見。 Atomic:Atomic方法對該變數的操作是原子性操作,顆粒度是到對這個變數的一次操作。 private stati

Java執行synchronized-鎖重入

1.什麼是鎖重入? 在使用synchronized時,當一個執行緒得到一個物件鎖後,再次請求此物件鎖時是可以再次得到該物件的鎖的。這也證明在一個synchronizes方法/塊的內部呼叫本類的其他synchronized方法/塊時,也是 永遠可以得到鎖的。

Java執行單例模式與執行

單例模式大家都不陌生,即讓一個類只有一個例項。 單例模式分為懶漢式和餓漢式。 懶漢式☞方法呼叫時再例項化物件,什麼時候用什麼時候例項化,比較懶。 餓漢式☞方法呼叫前物件就已經建立好了,比較有捉急。 本文著重描述懶漢式與多執行緒的內容。 1.餓漢式 public

Java執行synchronized與執行安全

介紹 修飾方法:一個執行緒去呼叫一個加synchronized的方法的時候,會獲得該物件的 物件鎖。 修飾靜態方法:一個執行緒去呼叫一個既加static,又加synchronized的方法的時候,會獲得該物件的 類鎖。 修飾程式碼塊: ①加物件鎖:

java執行執行的建立三種方式--筆記

申明:執行緒的概念以及程序的相關概念,可以參考網路上其他資料,這裡只討論多執行緒是怎麼實現。 一、多執行緒的簡單理解 明白什麼是多執行緒,小生通俗一點的理解為:在一個程式裡,我想同時讓這個程式完成多個任務。 比如:讓主函式 main 在列印1~100之間的所有整

Java執行執行同步機制

執行緒同步是為了確保執行緒安全,所謂執行緒安全指的是多個執行緒對同一資源進行訪問時,有可能產生資料不一致問題,導致執行緒訪問的資源並不是安全的。如果多執行緒程式執行結果和單執行緒執行的結果是一樣的,且相

Java執行Executor框架<Callable、Future、Executor和ExecutorService>

引言 Executor框架是指JDK 1.5中引入的一系列併發庫中與Executor相關的功能類,包括Executor、Exec