java線程池核心類ThreadPoolExecutor概述

2021-01-10 java技術閱讀

前言

前文JAVA中的阻塞隊列和非阻塞隊列我們介紹了常用的幾種隊列,隊列的使用很廣泛,特別是一些需要生產消費模式的場景以及需要對全局的集合進行操作的場景。今天我們來講講其中的一種應用——線程池。

有三種常見的創建線程的方法:繼承Thread類、實現Runnable接口和實現Callable接口。這些線程在運行結束後都會被虛擬機銷毀,如果線程數量多的話,頻繁的創建和銷毀線程會大大浪費時間和效率。更重要的是浪費內存,當線程執行完畢後死亡,線程對象就變成垃圾,造成GC的頻繁收集和停頓。

我們使用線程池來解決這個問題,讓線程運行完不立即銷毀,並且重複使用,繼續執行其他的任務。使用線程池來管理線程,一方面使線程的創建更加規範,可以合理控制開闢線程的數量;另一方面線程的細節管理交給線程池處理,優化了資源的開銷。

核心類

在java.util.concurrent包中我們能找到線程池的定義,其中ThreadPoolExecutor是我們線程池的核心類,我們先看下構造函數。

構造函數的參數含義:

corePoolSize:指定了線程池中的線程數量,它的數量決定了添加的任務是開闢新的線程去執行,還是放到workQueue任務隊列中去;maximumPoolSize:指定了線程池中的最大線程數量,這個參數會根據你使用的workQueue任務隊列的類型,決定線程池會開闢的最大線程數量;keepAliveTime:當線程池中空閒線程數量超過corePoolSize時,多餘的線程會在多長時間內被銷毀;unit:keepAliveTime的單位;workQueue:存放提交的任務,實現隊列的方式有:BlockingQueue、LinkedBlockingQueue、SynchronousQueue、SynchronousQueue等,關於隊列的選擇要根據實際情況來確定;threadFactory:線程工廠,用於創建線程,一般用默認即可;handler:拒絕策略;創建線程時,為了防止資源被耗盡,任務隊列都會選擇創建有界任務隊列,但這種模式下如果出現隊列已滿且線程池創建的線程數達到最大的線程數時,就需要用拒絕策略來處理線程池「超載」的情況。jdk默認的處理方式是AbortPolicy,拋出異常阻止程序(除非是安全性要求極高,否則在大並發情況下使用這種做法不是很明智);DiscardPolicy,丟棄無法處理的任務;DiscardOledesPolicy,也是丟棄任務,只不過丟棄的是隊列最先被添加進去,馬上要執行的任務;CallerRunsPolicy,由調用者所在線程來運行任務。除了使用jdk提供的這四種策略之外,我們還可以通過實現RejectExecutionHandler來自定義拒絕策略。一般流程圖:

當線程池中線程數小於corePoolSize時,新提交的任務將創建一個新線程執行任務,即使此時線程池中存在空閒線程;當線程池中線程數達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行 ;當workQueue已滿,且maximumPoolSize > corePoolSize時,新提交任務會創建新線程執行任務;當workQueue已滿,且提交任務數超過maximumPoolSize,任務由RejectedExecutionHandler處理;當線程池中線程數超過corePoolSize,且超過這部分的空閒時間達到keepAliveTime時,回收這些線程;當設置allowCoreThreadTimeOut(true)時,線程池中corePoolSize範圍內的線程空閒時間達到keepAliveTime也將回收。提交線程池

兩種方式的區別:execute沒有返回值,如果不需要知道線程的結果就使用execute方法,性能比較好;submit返回一個Future對象如果想知道線程結果就使用submit提交,而且它能在主線程中通過Future的get方法捕獲線程中的異常。

關閉線程池

兩種方式的區別:shutdown不再接受新的任務,之前提交的任務等執行結束再關閉線程池;shutdownNow不再接受新的任務,試圖停止池中的任務在關閉線程池,返回所有未處理的線程List列表。

總結

線程池主要用來解決線程生命周期開銷問題和資源不足問題。通過對多個任務重複使用線程,線程創建的開銷就被分攤到多個任務上,而且由於在請求到達時線程已經存在,所以消除線程創建所帶來的延遲。這樣,就可以立即為請求服務,使應用程式響應更快。另外,通過適當的調整線程中的線程數目可以防止出現資源不足。

相關焦點

  • Java8線程池ThreadPoolExecutor底層原理及其源碼解析
    線程池核心參數介紹下面將結合線程池中的任務提交流程加深理解.3.提交任務到線程池中的流程3.1 ThreadPoolExecutor#execute方法整體流程這裡以java.util.concurrent.ThreadPoolExecutor#execute方法為例, 畫一個簡單的圖:上圖中的worker可簡單理解為線程池中的一個線程, workers.size
  • 「原創」Java並發編程系列33|深入理解線程池(上)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫並發編程必不可少的線程池,接下來分兩篇文章介紹線程池,本文是第一篇。= threadFactory; this.handler = handler;}corePoolSize:核心線程數量;maximumPoolSize:
  • 線程池中使用InheritableThreadLocal遇到的坑
    現象其中一個 bug 就是使用 InheritableThreadLocal 造成了,我們在使用InheritableThreadLocal 的時候是為了在父子線程之間傳值,但是我們是使用的線程池,就造成了從 InheritableThreadLocal 取不到值的問題
  • 講真 這次絕對讓你輕鬆學習線程池
    簡單來說就是初始化N個線程充當線程池然後一起去阻塞隊列中進行阻塞take,新添加的任務都通過put將任務追加到任務隊列,關於任務隊列的講解看這blog核心類當運行當線程數= corePoolSize時,新的任務會被添加到中,如果也滿了則嘗試用非核心線程執行任務,另外等待隊列儘量用有界的哦!!threadFactory創建一個新線程時使用的工廠,可以用來設定線程名、是否為daemon線程等等。
  • 三萬字總結最全Java線程池源碼面試題
    1 為什麼要用線程池1.1 線程the more, the better?1、線程在java中是一個對象,更是作業系統的資源,線程創建、銷毀都需要時間。如果創建時間+銷毀時間>執行任務時間就很不合算。
  • 「原創」Java並發編程系列36|FutureTask
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫線程池源碼中出現了很多Callable、Future、FutureTask等以前沒介紹過的接口,尤其是線程池提交任務時總是把任務封裝成FutureTask,今天就來為大家解惑:
  • 2020年6月最新BAT一線大廠JAVA崗高頻面試題:阿里+華為+字節跳動
    11.對象存在java內存的那塊區域裡面12.多線程,AtomicInteger底層用的啥?cas的原理,AtomicInteger用了Voliate麼?voliate的原理,變量加Voliate被修改了其他線程能立刻知道麼?
  • Java日常開發的21個坑,你踩過幾個?
    六類典型空指針問題ConcurrentHashMap 這樣的容器不支持 Key 和 Value 為 null。 "main" java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:148) at java.util.AbstractList.add(AbstractList.java:108) at object.ArrayAsListTest.main(ArrayAsListTest.java
  • 面試官:聽說你看過ThreadLocal源碼?我來瞅瞅?
    ThreadLocal的數據結構image.pngThread類有一個類型為ThreadLocal.ThreadLocalMap的實例變量threadLocals,也就是說每個線程有一個自己的ThreadLocalMap。
  • 深入理解 Java 並發核心機制,看完後好爽
    一、J.U.C 簡介Java 的 java.util.concurrent 包(簡稱 J.U.C)中提供了大量並發工具類,是 Java 並發能力的主要體現(注意,不是全部,有部分並發能力的支持在其他包中)。
  • 原創】Java並發編程系列01|開篇獲獎感言
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫為什麼要學並發編程我曾聽一個從事15年開發工作的技術人員說過4.CAS原子操作:並發編程的基礎與核心CAS的實現原理,以及Java中的CAS原子操作。5.Lock體系:JDK的Lock對於synchronized有哪些優勢;Lock如何通過AQS與LockSupport工具實現的;Lock的使用。
  • 動力節點Java學院2021年Java學習路線圖最新出爐啦
    我們首先要接觸的就是Java環境搭建,Java核心語法。重點學習:面向對象,集合、IO流、線程、並發、異常及網絡編程等等第二階段:資料庫重點學習:Tomcat伺服器、Jsp、EL表達式、異步AJAX請求,MVC架構模式,線程池原理第五階段:Javaweb項目
  • Java之Thread和Runnable接口的區別
    Java之創建多線程的第二種方法,實現類,Java之創建多線程的第一種方式,thread類。這次小編要講的是這兩種方式的區別。其實就是實現Runnable接口創建多線程的好處,具體如下:避免了單繼承的局限性,一個類只能繼承一個類,這個類繼承了Thread類就不能繼承其它類。但是,實現了Runnable接口,還可以繼承其它的類,實現其它接口。
  • java生成隨機數的五種方法
    (randomNumberGenerator = new Random()) : rnd; // 實際上用的是new java.util.Random()}This method is properly synchronized to allow correct use by more than one thread.
  • GitHub上訪問下載破百萬的神仙文檔《Java面試神技》看完我呆了!
    內容簡介JM面試題1、java中會存在內存洩漏嗎,請簡單描述。2、64位JYM中,int的長度是多數?4、ExecutorService、 Callable、 Future 有返回值線程5、基於線程池的方式6、4種線程池7、如何停止- -個正在運行的線程
  • java基礎教程:Collection集合,Collection 常用API
    集合概述在前面基礎班我們已經學習過並使用過集合ArrayList<E> ,那麼集合到底是什麼呢?集合:集合是java中提供的一種容器,可以用來存儲多個數據。集合和數組既然都是容器,它們有什麼區別呢?數組的長度是固定的。集合的長度是可變的。
  • java面試中必問的oom問題
    2. java.lang.OutOfMemoryError: Java heap space溢出原因:深入理解Java虛擬機書中講到java堆溢出,Java堆用於存儲對象實例,只要不斷地創建對象,並且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼在對象數量到達最大堆的容量限制後就會產生內存溢出異常
  • Java資料庫連接性簡介
    調用此文件WhatIsJdbc.java。下次執行Java程序時,將.jar通過類路徑將該文件拉入。有幾種設置類路徑的方法。清單3顯示了如何使用命令行開關來做到這一點。清單3.在Java類路徑上執行SQLite驅動程序java.exe -classpath /path-to-driver/sqlite-jdbc-3.23.1.jar:. WhatIsJdbc注意,我們將類路徑設置為指向驅動程序和本地目錄。
  • Java之File類的構造方法
    Java之File類的簡單介紹,File類的靜態成員變量,這次小編要介紹的是File類的構造方法(f1);//重寫了Object類的toString方法,列印的是一個路徑:c:\Users\java\code\a.textFile f2=new File("c:\\Users\\java\\code");System.out.println(f2);