這期內容當中小編將會給大家帶來有關java線程池的原理和應用,以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
線程池概述
1. 線程池就是一個管理線程的池子,可以降低創建和銷毀線程帶來的資源消耗
因為線程其實也是一個對象,創建一個對象,需要經過類加載過程,銷毀一個對象,需要走GC垃圾回收流程,都是需要資源開銷的。
2. 提高響應速度,任務到達了相對于從線程池取線程,自己創建線程肯定慢很多
3. 重復利用,線程用完了再放回池子,達到了重復利用的效果
線程池執行
打個比喻
核心線程比作公司正式員工
非核心線程比作外包員工
阻塞隊列比作需求池
提交任務比作提需求
正式執行
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize 核心線程數 maximumPoolSize 線程池最大線程數 keepAliveTime 空閑線程存活時間 TimeUnit 線程空閑存活時間單位 workQueue 存放任務的阻塞隊列 threadFactory 線程工廠 handler 飽和策略
● 提交一個任務,線程池里存活的核心線程數小于線程數corePoolSize時,線程池會創建一個核心線程去處理提交的任務。
● 如果線程池核心線程數已滿,即線程數已經等于corePoolSize,一個新提交的任務,會被放進任務隊列workQueue排隊等待執行。
● 當線程池里面存活的線程數已經等于corePoolSize了,并且任務隊列workQueue也滿,判斷線程數是否達到maximumPoolSize,即最大線程數是否已滿,如果沒到達,創建一個非核心線程執行提交的任務。
● 如果當前的線程數達到了maximumPoolSize,還有新的任務過來的話,直接采用拒絕策略處理。
幾種飽和策略
AbortPolicy 拋出一個異常,默認的 DiscardPolicy 直接丟棄任務 DiscardOldestPolicy 丟棄隊列里最老的任務,將當前這個任務繼續提交給線程池 CallerRunsPolicy 交給線程池調用所在的線程進行處理
線程池異常處理
由于在線程池調用線程處理任務過程中出現的異??赡軙痪€程池捕獲,所以對于任務的執行可能是無感知的,因此我們需要考慮線程池異常情況。
方法一:
@Test public void test1() throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { executorService.submit(new Runnable() { @Override public void run() { try { System.out.println("name: " + Thread.currentThread().getName()); Object a = null; System.out.println(a.hashCode()); } catch (Exception e) { System.out.println(e); } } }); } }
方法二:
@Test public void test2() throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 20; i++) { Future<?> future = executorService.submit(new Runnable() { @Override public void run() { System.out.println("name: " + Thread.currentThread().getName()); Object a = null; System.out.println(a.hashCode()); } }); try { future.get(); } catch (Exception e) { System.out.println(e); } } }
線程池的工作隊列
● ArrayBlockingQueue
● LinkedBlockingQueue
● SynchronousQueue
● DelayQueue
● PriorityBlockingQueue
==ArrayBlockingQueue==
● 初始化一定容量的數組
● 使用一個重入鎖,默認使用非公平鎖,入隊和出隊共用一個鎖,互斥
● 是有界設計,如果容量滿無法繼續添加元素直至有元素被移除
● 使用時開辟一段連續的內存,如果初始化容量過大容易造成資源浪費,過小易添加失敗
==LinkedBlockingQueue==
● 使用鏈表數據結構
● 非連續性內存空間
● 使用兩個重入鎖分別控制元素的入隊和出隊,用Condition進行線程間的喚醒和等待
● 有邊界的,在默認構造方法中容量是Integer.MAX_VALUE
==SynchronousQueue==
● 內部容量是0
● 每次刪除操作都要等待插入操作
● 每次插入操作都要等待刪除操作
● 一個元素,一旦有了插入線程和移除線程,那么很快由插入線程移交給移除線程,這個容器相當于通道,本身不存儲元素
● 在多任務隊列,是最快的處理任務方式。
==PriorityBlockingQueue==
● 無邊界設計,但容量實際是依靠系統資源影響
● 添加元素,如果超過1,則進入優先級排序
==DelayQueue==
● 無邊界設計
● 添加(put)不阻塞,移除阻塞
● 元素都有一個過期時間
● 取元素只有過期的才會被取出
常用的線程池
● newFixedThreadPool (固定數目線程的線程池)
● newCachedThreadPool (可緩存線程的線程池)
● newSingleThreadExecutor (單線程的線程池)
● newScheduledThreadPool (定時及周期執行的線程池)
==newFixedThreadPool==
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
特點
1. 核心線程數和最大線程數大小一樣
2. 沒有所謂的非空閑時間,即keepAliveTime為0
3. 阻塞隊列為無界隊列LinkedBlockingQueue
工作機制:
● 提交任務
● 如果線程數少于核心線程,創建核心線程執行任務
● 如果線程數等于核心線程,把任務添加到LinkedBlockingQueue阻塞隊列
● 如果線程執行完任務,去阻塞隊列取任務,繼續執行。
==newCachedThreadPool==
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
線程池特點
● 核心線程數為0
● 最大線程數為Integer.MAX_VALUE
● 阻塞隊列是SynchronousQueue
● 非核心線程空閑存活時間為60秒
工作機制:
● 提交任務
● 因為沒有核心線程,所以任務直接加到SynchronousQueue隊列。
● 判斷是否有空閑線程,如果有,就去取出任務執行。
● 如果沒有空閑線程,就新建一個線程執行。
● 執行完任務的線程,還可以存活60秒,如果在這期間,接到任務,可以繼續活下去;否則,被銷毀。
使用場景
用于并發執行大量短期的小任務。
使用SynchronousQueue作為工作隊列,工作隊列本身并不限制待執行的任務的數量。但此時需要限定線程池的最大大小為一個合理的有限值,而不是Integer.MAX_VALUE,否則可能導致線程池中的工作者線程的數量一直增加到系統資源所無法承受為止。
如果應用程序確實需要比較大的工作隊列容量,而又想避免無界工作隊列可能導致的問題,不妨考慮SynchronousQueue。SynchronousQueue實現上并不使用緩存空間
==newSingleThreadExecutor==
線程池特點
● 核心線程數為1
● 最大線程數也為1
● 阻塞隊列是LinkedBlockingQueue
● keepAliveTime為0
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }
工作機制
● 提交任務
● 線程池是否有一條線程在,如果沒有,新建線程執行任務
● 如果有,講任務加到阻塞隊列
● 當前的唯一線程,從隊列取任務,執行完一個,再繼續取,一個人(一條線程)夜以繼日地干活。
使用場景
適用于串行執行任務的場景,一個任務一個任務的執行
==newScheduledThreadPool==
線程池特點
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue()); }
● 最大線程數為Integer.MAX_VALUE
● 阻塞隊列是DelayedWorkQueue
● keepAliveTime為0
● scheduleAtFixedRate() :按某種速率周期執行
● scheduleWithFixedDelay():在某個延遲后執行
工作機制
● 添加一個任務
● 線程池中的線程從 DelayQueue 中取任務
● 線程從 DelayQueue 中獲取 time 大于等于當前時間的task
● 執行完后修改這個 task 的 time 為下次被執行的時間
● 這個 task 放回DelayQueue隊列中
scheduleWithFixedDelay
● 無論任務執行時間長短,都是當第一個任務執行完成之后,延遲指定時間再開始執行第二個任務
scheduleAtFixedRate
● 在任務執行時間小于間隔時間的情況下,程序以起始時間為準則,每隔指定時間執行一次,不受任務執行時間影響
● 當執行任務時間大于間隔時間,此方法不會重新開啟一個新的任務進行執行,而是等待原有任務執行完成,馬上開啟下一個任務進行執行。此時,執行間隔時間已經被打亂
上述就是小編為大家分享的java線程池的原理和應用了,如果您也有類似的疑惑,不妨參照上述方法進行嘗試。如果想了解更多相關內容,請關注億速云行業資訊。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。