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