溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

java線程池的原理和應(yīng)用

發(fā)布時(shí)間:2020-06-24 09:16:48 來源:億速云 閱讀:134 作者:Leah 欄目:編程語(yǔ)言

這期內(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ù)比作提需求

java線程池的原理和應(yīng)用

正式執(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ī)制:

java線程池的原理和應(yīng)用

● 提交任務(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秒

java線程池的原理和應(yīng)用

工作機(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ī)制

java線程池的原理和應(yīng)用

● 提交任務(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è)資訊。

向AI問一下細(xì)節(jié)

免責(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)容。

AI