Android 線程池的類型、區別以及為何要用線程池

来源:https://www.cnblogs.com/cspecialy/archive/2018/05/26/9093182.html
-Advertisement-
Play Games

每個 Android 應用進程在創建時,會同時創建一個線程,我們稱之為主線程,負責更新 UI 界面以及和處理用戶之間的交互,因此,在 Android 中,我們又稱之為 UI 線程。一個進程中 UI 線程只有一個,為了不造成界面卡頓、提高用戶體驗,我們勢必要將一些耗時操作交由子線程來執行。 使用子線程 ...


每個 Android 應用進程在創建時,會同時創建一個線程,我們稱之為主線程,負責更新 UI 界面以及和處理用戶之間的交互,因此,在 Android 中,我們又稱之為 UI 線程。一個進程中 UI 線程只有一個,為了不造成界面卡頓、提高用戶體驗,我們勢必要將一些耗時操作交由子線程來執行。

使用子線程的方式主要分兩種:

  • 直接使用 ThreadRunnable 等創建子並使用線程

  • 使用線程池創建並使用子線程

線程池是什麼

線程池是指在初始化一個多線程應用程式過程中創建一個線程集合,然後在需要執行新的任務時重用這些線程而不是新創建一個線程。線程池中線程的數量通常完全取決於可用記憶體數量和應用程式的需求;每個線程都有被分配一個任務,一旦任務完成了,線程回到池子中並等待下一次分配任務。

一般情況下,推薦使用線程池來創建和使用子線程,不建議使用第一種方式。

為何要用線程池

上面說了,在 Android 開發過程中,建議使用線程池來創建和使用子線程,那麼使用線程池的好處有哪些呢?

  • 線程池改進了一個應用程式的響應時間。由於線程池中的線程已經準備好且等待被分配任務,應用程式可以直接拿來使用而不用新建一個線程。

  • 線程池節省了 CLR 為每個短生存周期任務創建一個完整的線程開銷並可以在任務完成後回收資源。

  • 線程池根據當前在系統中運行的進程來優化線程片。

  • 線程池允許我們開啟多個任務而不用為每個線程設置屬性。

  • 線程池允許我們為正在執行的任務的程式參數傳遞一個包含狀態信息的對象引用。

  • 線程池可以用來解決處理一個特定請求最大線程數量限制問題。

  • 系統分配給每個應用的線程棧是固定的,使用線程池可以有效地避免線程棧溢出引起的應用崩潰。

Android 中的線程池

Android 中線程池的真正實現是 ThreadPoolExecutor,其構造方法有 5 個,通過一系列參數來配置線程池。下麵介紹一個比較常用的構造方法及其參數的含義。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)
 
參數 含義
corePoolSize int: 線程池的核心線程數,預設情況下,核心線程回一直線上程池中存活,即使他們處於閑置狀態。如果將 allowCoreThreadTimeOut 的屬性設置為 true,那麼閑置的核心線程在等待新任務到來時會有超時策略,這個時間間隔由 keepAliveTime 所指定,當等待時間超過 keepAliveTime 所指定的時長後,核心線程就會被終止。
maximumPoolSize int: 線程池中允許的線程最大數量,當活動線程達到這個數值後,後續的新任務會被阻塞。int: 線程池中允許的線程最大數量,當活動線程達到這個數值後,後續的新任務會被阻塞。
keepAliveTime long: 非核心線程閑置時的超時時長,超過這個時長,非核心線程就會被回收。當 allowCoreThreadTimeOut 屬性被設置為 true 時,該參數同樣會作用於核心線程。
unit TimeUnit: keepAliveTime 參數的時間單位 ,參數為 TimeUnit 的枚舉,常見的有 TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECOND(秒) 等。
workQueue BlockingQueue: 線程池中的任務隊列,通過線程池的 execute 方法提交的 Runnable 對象會存儲在這個參數中。
threadFactory ThreadFactory: 創建線程的線程工廠。ThreadFactory 是一個介面,只有一個方法:Thread newThread (Runnable r)

 

 
Throws 
IllegalArgumentException

符合以下任一條件,則拋出此異常:

corePoolSize < 0

keepAliveTime < 0

maximumPoolSize <= 0

maximumPoolSize < corePoolSize

NullPointerException workQueue 或者 threadFactory 為 null 時,拋出此異常。

ThreadPoolExecutor 執行任務時大致遵循如下規則:

  1. 如果線程池中的線程數量未達到核心線程的數量,那麼會直接啟動一個核心線程來執行任務。

  2. 如果線程池中的線程數量已經達到或超過核心線程的數量,那麼任務會被插入到任務隊列中等待執行。

  3. 如果步驟 2 中無法將任務插入到任務隊列,則表示任務隊列已滿。此時,如果線程數量未達到 maximumPoolSize 值,則會立即啟動一個非核心線程來執行任務。

  4. 如果步驟 3 中線程數量大於或等於 maximumPoolSize 值,則拒絕執行次任務,此時 ThreadPoolExecutor 會調用 RejectedExecutionHandlerrejectedExecution 來通知調用者。

RejectedExecutionHandler 是線程池持有的一個對象,用於不能由 ThreadPoolExecutor 執行的任務的處理

Android 中線程池的類型及區別

Android 中最常見線程池有四種,分別是:FixedThreadPoolCacheThreadPoolScheduledThreadPool 以及 SingleThreadPool,它們都直接或間接的通過配置 ThreadPoolExecutor 來實現自己的功能特性。

1. FixedThreadPool

線程數量固定的線程池,通過 ExecutorsnewFixedThreadPool 方法創建。有兩個重載的創建方法:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}

除非線程池被關閉,否則線程不會被回收,即時線程處於空閑狀態。如果在所有線程都處於活動狀態時提交額外的任務,它們將在隊列中等待,直到有一個線程可用為止。由創建方法可知,FixedThreadPool 只有核心線程並且這個核心線程沒有超時機制(keepAliveTime 參數為 0L),加上線程不會被回收,因此使用此類線程池可以快速地響應外界的請求。

2. CacheThreadPool

線程數量不定的線程池,通過 ExecutorsnewCachedThreadPool 方法創建。有兩個重載的創建方法:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(/* corePoolSize*/ 0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(/* corePoolSize*/ 0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }

CacheThreadPool 有以下個特征:

  • 沒有核心線程( corePoolSize 參數為 0 ),只有非核心線程且非核心線程的數量為 Integer.MAX_VALUE,這就相當於非核心線程的數量可以無限大。

  • 線程池的線程處於空閑狀態時,線程池會重用空閑的線程來處理新任務,否則創建新的線程來處理,新創建的線程會添加到線程池中。這將提高執行許多短期非同步任務的程式性能。

  • 閑置時間超過 60 秒的空閑線程會被回收(keepAliveTime 參數為 60L )。因此,閑置時間足夠長的 CacheThreadPool 也不會消耗任何系統資源。

3. ScheduledThreadPool

核心線程數量固定,非核心線程數量不定的線程池,通過 Executorsnewscheduledthreadpool 方法創建。有兩個重載的創建方法:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

ScheduledThreadPool 相比較其他三種線程池,有特殊性,由 ScheduledThreadPoolExecutor 實現, newscheduledthreadpool 方法也是通過創建 ScheduledThreadPoolExecutor 的實例來完成線程池的創建,代碼如下:

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory);
}

ScheduledThreadPoolExecutor 的構造函數可知, ScheduledThreadPool 的核心線程數量是固定的,由傳入的 corePoolSize 參數決定,非核心線程數量可以無限大。非核心線程閑置回收的超時時間為 10秒DEFAULT_KEEPALIVE_MILLIS 的值為 10L )。這類線程主要用於執行定時任務或者具有周期性的重覆任務。

4. SingleThreadPool

只有一個核心線程,通過 Executorsnewsinglethreadexecutor 方法創建。有兩個重載的創建方法:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(/* corePoolSize*/ 1, /* maximumPoolSize*/ 1,
                                /* keepAliveTime*/ 0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(/* corePoolSize*/ 1, /* maximumPoolSize*/ 1,
                                /* keepAliveTime*/ 0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}

SingleThreadPool 由 代理類 FinalizableDelegatedExecutorService 創建。該線程池只有一個線程(核心線程),並且該線程池的任務隊列是無上限的,這就確保了所有的任務都在同一個線程中順序執行。

註意,如果由於在執行期間出現故障而導致該線程終止,那麼如果需要執行後續任務,則新線程將取而代之。

四類線程池的區別

上面分別對 Android 中常見的 4 種線程池進行了簡單的介紹,除了這 4 種系統提供的線程池外,我們在使用的過程中,也可以根據需要直接通過 ThreadPoolExecutor 的構造函數來靈活的配置線程池。那麼,上述的 4 種線程池,其區別在哪呢?瞭解其區別有助於我們去選擇更為合適的線程池或者直接通過 ThreadPoolExecutor 來配置更靈活的線程池。

FixedThreadPool 線程固定,且不會被回收,能夠更快的響應外界請求。

CachedThreadPool 只有非核心線程,且線程數相當於無限大,任何任務都會被立即執行。比較適合執行大量的耗時較少的任務。

ScheduledThreadPool 主要用於執行定時任務或者具有周期性的重覆任務。

SingleThreadPool 只有一個核心線程,確保所有任務都在同一線程中按順序完成。因此不需要處理線程同步的問題。

 

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、下載oracle VM virtualbox centos7 1.1、 下載地址:https://www.virtualbox.org/wiki/Downloads https://www.centos.org/download/ 1.2 安裝教程:https://jingyan.baidu.c ...
  • VirtualBox圖形界面下有四種網路接入方式,它們分別是: 1、NAT 網路地址轉換模式(NAT,Network Address Translation) 2、Bridged Adapter 橋接模式 3、Internal 內部網路模式 4、Host-only Adapter 主機模式 而在Co ...
  • 功能簡介: 操作方法: 溫馨提示: ...
  • 1.首先查看時區:swfsadmin@swfsubuntu:~$ date -RTue, 17 Dec 2013 18:23:01 +0800如果要修改時區,執行sudo tzselect2.選擇區域:亞洲swfsadmin@swfsubuntu:~$ sudo tzselect[sudo] pas ...
  • 使用MySQL 登錄、連接資料庫 win+R打開控制台,cmd進入控制台,輸入 ,後輸入密碼,進入資料庫; 首先可以查看原有的資料庫,輸入 好的,現在開始創建一個新的資料庫,命名為crashcourse, 由於上課時老師提供了兩個sql文件,將sql文件導入資料庫中即可,導入時的語句 ,`sourc ...
  • <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 --> <property name="acquireIncrement">3</property> <!--定義在從資料庫獲取新連接失敗後重覆嘗試的次數。Default: 30 --> <property nam ...
  • 概念:類似於java的方法,將一組邏輯語句封裝在方法體中,對外暴露方法名 好處:1、隱藏了實現細節 2、提高代碼的重用性 調用:select 函數名(實參列表) 【from 表】; 1、字元函數 1.1、length 獲取參數值的位元組個數 SELECT LENGTH('john'); 1.2、con ...
  • GTID複製典型的複製錯誤有兩種:1,數據對象級別的錯誤,包括主庫上update的數據在從庫上不存在,主從逐漸衝突,庫表索引等對象的衝突等等, 如果是純粹的跳過錯誤的話,這一類的錯誤需要跳過思路是找到主庫binlog中對應的事務Id然後在從庫上跳過即可。2,日誌找不到的錯誤,也即從庫在執行利用主庫上 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...