Java線程池詳解及常用方法

2020-12-13 計算機java編程

前言

最近被問到了線程池的相關問題。於是準備開始寫一些多線程相關的文章。這篇將介紹一下線程池的基本使用。

Executors

Executors是concurrent包下的一個類,為我們提供了創建線程池的簡便方法。

Executors可以創建我們常用的四種線程池:

(1)newCachedThreadPool 創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。不設上限,提交的任務將立即執行。

(2)newFixedThreadPool 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。

(3)newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。

(4)newSingleThreadExecutor 創建一個單線程化的線程池執行任務。

Executors的壞處

正常來說,我們不應該使用這種方式創建線程池,應該使用ThreadPoolExecutor來創建線程池。Executors創建的線程池也是調用的ThreadPoolExcutor的構造函數。通過原來可以看出。

我們也看到了這裡面的LinkedBlockingQueue並沒有指定隊列的大小是一個無界隊列,這樣可能會造成oom。所以我們要使用ThreadPoolExecutor這種方式。

ThreadPoolExecutor

通過源碼看到ThreadPoolExecutor比較全的構造函數如下:

分別解釋一下參數的意義

corePoolSize:線程池長期維持的線程數,即使線程處於Idle狀態,也不會回收。

maximumPoolSize:線程數的上限

keepAliveTime:空閒的時間,超過這個空閒時間,線程將被回收

unit:空閒時間的時間單位

workQueue:任務的排隊隊列,當線程都運行的時候,有空的線程將從隊列匯總進行拿取

threadFactroy:當核心線程小於滿線程的時候,又需要多加線程,則需要從工廠中獲取線程

handler:拒絕策略,當線程過多的時候的策略

線程池針對於任務的執行順序

首先任務過來之後,看看corePoolSize是否有空閒的,有的話就執行。沒有的話,放入任務隊列裡面。然後任務隊列會通知線程工廠,趕緊造幾個線程,來執行。當任務超過了最大的線程數,就執行拒絕策略,拒絕執行。

submit方法

線程池建立完畢之後,我們就需要往線程池提交任務。通過線程池的submit方法即可。

submit方法接收兩種Runable和Callable。

區別如下:

Runable是實現該接口的run方法,callable是實現接口的call方法。

callable允許使用返回值。

callable允許拋出異常。

提交任務的方式

Future

blockqueue的限制

我們在創建線程池的時候,如果使用Executors。創建的是無界隊列,容易造成oom。所以我們要自己執行queue的大小。

BlockingQueue queue = new ArrayBlockingQueue<>(512)

拒絕策略

當任務隊列的queue滿了的時候,在提交任務,就要觸發拒絕策略。隊列中默認的拒絕策略是 AbortPolicy。是直接拋出異常的一種策略。

如果是想實現自定義的策略,可以實現RejectedExecutionHandler 接口。

線程池提供了如下的幾種策略供選擇。

AbortPolicy:默認策略,拋出RejectedExecutionException

DiscardPolicy:忽略當前提交的任務

DiscardOldestPolicy:丟棄任務隊列中最老的任務,給新任務騰出地方

CallerRunsPolicy:由提交任務者執行這個任務

ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(512), new ThreadPoolExecutor.DiscardPolicy());

捕捉異常

如之前所說Callable接口的實現,可以獲取到結果和異常。通過返回的Future的get方法即可拿到。

正確構造線程池的方式

獲取單個結果

通過submit提交一個任務後,可以獲取到一個future,調用get方法會阻塞並等待執行結果。get(long timeout, TimeUnit unit)可以指定等待的超時時間。

獲取多個結果

可以使用循環依次調用,也可以使用ExecutorCompletionService。該類的take方式,會阻塞等待某一任務完成。向CompletionService批量提交任務後,只需調用相同次數的CompletionService.take()方法,就能獲取所有任務的執行結果,獲取順序是任意的,取決於任務的完成順序。

這個類是對線程池的一個包裝,包裝完後,聽過他進行submit和take。

單個任務超時

Future.get(long timeout, TimeUnit unit)。方法可以指定等待的超時時間,超時未完成會拋出TimeoutException。

多個任務超時

等待多個任務完成,並設置最大等待時間,可以通過CountDownLatch完成:

await是總的時間,即使100個任務,需要跑20分鐘。我10s超時了 也停止了。

相關焦點

  • Java之線程池的簡單介紹
    現在小編來說說線程池的代碼實現:首先,java.util.concurrent.Executors:線程池的工廠類,用來生成線程池其次,Executors類中的靜態方法:>ExecutorService接口,返回的是ExecutorService接口的實現類對象,我們可以使用ExecutorService接口接收(面向接口編程)java.util.concurrent.ExecutorService:線程池接口,用來從線程池中獲取線程,調用start方法,執行線程任務。
  • 【線程池】java線程池ThreadPoolExecutor
    可選的參數為java.util.concurrent.TimeUnit中的幾個靜態屬性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。    workQueue 存放通過execute方法提交給線程等待執行的任務的隊列。    threadFactory 負責給線程池創建線程的工廠。
  • Java中線程池,你真的會用嗎?
    到底應該如何創建一個線程池呢?Executors 是一個Java中的工具類。提供工廠方法來創建不同類型的線程池。從上圖中也可以看出,Executors的創建線程池的方法,創建出來的線程池都實現了ExecutorService接口。常用方法有以下幾個:newFiexedThreadPool(int Threads):創建固定數目線程的線程池。
  • java新手揭秘:阿里巴巴為何禁止使用Executors來創建線程池
    當一個java新手從不斷地Curd階段跳出來之後,就會學習java的並發,並行等高階用法,自然就會用到線程、線程池,線程池的好處這裡就不做詳細解釋,你應該會學習到Executors創建線程池的四個方法, 分別是:newFixedThreadPool
  • Java入門 - - - 線程池的基本使用
    線程池背景介紹舉例說明:假設,目前有1000(或者更多)個任務需要執行,傳統方法可能需要利用循環創建1000個線程分別執行這1000個任務。線程池基本使用1.創建線程:java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類,因此如果要透徹地了解Java中的線程池,必須先了解這個類。在ThreadPoolExecutor類中提供了四個構造方法:
  • java開發基礎 線程池詳解
    前言:我們在開發中經常用會到多線程,今天來介紹一下線程池的使用。線程池簡介1.5以後引入了Executor框架,它的內部使用了線程池機制,我們常使用這個框架來創建線程池。下圖為Executor下的接口和類繼承關係:Executor框架我們常用到的包括:線程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等Executors提供了一系列工廠方法用於創建線程池,返回的線程池都實現了ExecutorService接口。
  • 深入理解 Java 線程池!
    這種問題使用線程池便可以很好的解決。通過線程池線程,銷毀及回收等交由線程池進行管理,就可以避免以上的問題。ThreadFactory是一個接口,它只有一個用來創建線程的方法:Thread newThread(Runnable r);對於核心的幾個線程池,無論是newFixedThreadPool()方法,newSingleThreadExecutor()方法,還是newCacheThreadPool()方法,雖然看起來創建的線程有著完全不同的功能特點,
  • Java中線程池的簡單使用
    線程池可以用來幹什麼?線程池就可以幫助我們解決這個問題,他使線程可以重複使用,就是執行完一個任務線程不會被銷毀,而是可以繼續執行其他任務java中的線程池如何使用?java中線程池的使用java中有哪些線程池Java通過Executors提供四種線程池,分別為:
  • 手寫Java線程池(超詳細解說)
    決定是否執行拒絕策略(拒絕策略具有多個, 拒絕方式取決於用戶自定義, 在線程池內部具有默認的拒絕策略實現);任務隊列描述實現Runnable接口, 在run方法中獲取RunnableQueue中的任務, 然後執行RunnQueue中的任務, InternalTask中的run方法是一個while循環循環結束條件取決於是否關閉該線程(關閉線程據需要設置flag變量, 當flage為false
  • java線程池核心類ThreadPoolExecutor概述
    JAVA中的阻塞隊列和非阻塞隊列我們介紹了常用的幾種隊列,隊列的使用很廣泛,特別是一些需要生產消費模式的場景以及需要對全局的集合進行操作的場景。今天我們來講講其中的一種應用——線程池。有三種常見的創建線程的方法:繼承Thread類、實現Runnable接口和實現Callable接口。這些線程在運行結束後都會被虛擬機銷毀,如果線程數量多的話,頻繁的創建和銷毀線程會大大浪費時間和效率。更重要的是浪費內存,當線程執行完畢後死亡,線程對象就變成垃圾,造成GC的頻繁收集和停頓。
  • 詳解 Tomcat 的連接數與線程池
    在前面的文章 詳解Tomcat配置文件server.xml 中寫到過:Connector的主要功能,是接收連接請求,創建Request和Response對象用於和請求端交換數據;然後分配線程讓Engine(也就是Servlet容器)來處理這個請求,並把產生的Request和Response對象傳給Engine。
  • 七個方面帶你玩轉Java線程池
    到此,我們學會了ThreadPoolExecutor的構造方法的參數列表每個參數的含義,也就知道了如何去創建一個線程池。  3  向線程池提交任務  可以使用兩個方法向線程池提交任務,分別是execute()和submit()方法。
  • 深入理解 Java 線程池,講解的太清晰了
    三、ThreadPoolExecutorjava.uitl.concurrent.ThreadPoolExecutor 類是 Executor 框架中最核心的類。所以,本文將著重講述一下這個類。在線程池處於 RUNNING 狀態時,調用 shutdown 方法會使線程池進入到該狀態。finalize 方法在執行過程中也會調用 shutdown 方法進入該狀態。RUNNING - 運行狀態。
  • 使用Executors,ThreadPoolExecutor,創建線程池,源碼分析理解
    之前創建線程的時候都是用的 newCachedThreadPoo,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 這四個方法。
  • java開發之Tomcat線程池優化
    前言:上期我們說到了jvm的內存優化,這期我們來說說tomcat的線程池優化,此思路同樣可用於c3p0等連接池tomcat線程池優化我們tomcat線程池的優化,其實就是最大限度的發揮tomcat的性能。即讓伺服器在保障性能的情況下並發最大並發:所有線程,在同一秒一起訪問同一個資源。
  • Java並發編程系列34|深入理解線程池(下)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文是深入理解線程池下篇:線程池介紹Executor框架接口線程池狀態線程池參數線程池創建執行過程關閉線程池其他問題任務拒絕策略線程池中的線程初始化線程池容量的動態調整線程池的監控6.
  • 「原創」Java並發編程系列33|深入理解線程池(上)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫並發編程必不可少的線程池,接下來分兩篇文章介紹線程池,本文是第一篇。線程池狀態RUNNING :能接受新提交的任務,並且也能處理阻塞隊列中的任務;SHUTDOWN:關閉狀態,不再接受新提交的任務,但卻可以繼續處理阻塞隊列中已保存的任務。在線程池處於 RUNNING 狀態時,調用 shutdown()方法會使線程池進入到該狀態。
  • 從使用到原理,探究Java線程池
    什麼是線程池當我們需要處理某個任務的時候,可以新創建一個線程,讓線程去執行任務。線程池的字面意思就是存放線程的池子,當我們需要處理某個任務的時候,可以從線程池裡取出一條線程去執行。2.幾種常見的線程池分析Java為我們提供了幾種常用的線程池,通過Executors類可以輕易地獲取它們。下面我們通過分析這幾種常用線程池的參數,了解這些線程池之間的異同。
  • 通過源碼解析,深入Java 線程池原理
    除非有某種方法來打破對鎖的等待(Java 鎖定不支持這種方法),否則死鎖的線程將永遠等下去。資源不足線程池的一個優點在於:相對於其它替代調度機制(有些我們已經討論過)而言,它們通常執行得很好。但只有恰當地調整了線程池大小時才是這樣的。
  • 阿里P8大佬總結:Java線程池詳解,看了你就懂
    Executors類只是個靜態工廠,提供創建線程池的幾個靜態方法(內部屏蔽了線程池參數配置細節),而真正的線程池類是ThreadPoolExecutor。ThreadPoolExecutor構造方法如下:參數解釋:corePoolSize:核心線程數。