摘要:中的線程池是運(yùn)用場景最多的并發(fā)框架。才是真正的線程池。存放任務(wù)的隊(duì)列存放需要被線程池執(zhí)行的線程隊(duì)列。所以線程池的所有任務(wù)完成后,它最終會(huì)收縮到的大小。飽和策略一般情況下,線程池采用的是,表示無法處理新任務(wù)時(shí)拋出異常。
Java線程池 1. 簡介
系統(tǒng)啟動(dòng)一個(gè)新線程的成本是比較高的,因?yàn)樗婕芭c操作系統(tǒng)的交互,這個(gè)時(shí)候使用線程池可以提升性能,尤其是需要?jiǎng)?chuàng)建大量聲明周期很短暫的線程時(shí)。Java中的線程池是運(yùn)用場景最多的并發(fā)框架。
線程池類似于數(shù)據(jù)庫連接池,在系統(tǒng)啟動(dòng)的時(shí)候即創(chuàng)建大量空閑的線程,可以將一個(gè)線程任務(wù)提交給線程池執(zhí)行,當(dāng)任務(wù)執(zhí)行完后,線程不會(huì)死亡,而是再次返回線程池中成為空閑狀態(tài)。
使用線程池的好處降低資源消耗:重復(fù)利用降低創(chuàng)建和銷毀線程的消耗;
提高響應(yīng)速度:任務(wù)來了可以理解執(zhí)行,不必等待線程創(chuàng)建;
提高線程的可管理性:使用線程池可以統(tǒng)一的分配、調(diào)優(yōu)和監(jiān)控。
2. 使用Executors工廠類來產(chǎn)生線程Executors工廠類主要有4種方式生產(chǎn)線程池:
1.newCachedThreadPool() :創(chuàng)建一個(gè)具有緩存功能的線程池,線程池?zé)o邊界,適用于執(zhí)行很多短期異步任務(wù)的小程序,或者負(fù)載較輕的服務(wù)器。
2. newFixedThreadPool(int nThreads):創(chuàng)建一個(gè)固定線程數(shù)的線程池,適用于為了滿足資源管理的要求而需要限制線程數(shù)量的場景,比如負(fù)載較重的服務(wù)器。
3. newSingleThreadExecutor():創(chuàng)建一個(gè)只有單線程的線程池,適用于需要保證順序地執(zhí)行各個(gè)任務(wù),并且在任意時(shí)間點(diǎn)不會(huì)有多個(gè)線程是活動(dòng)的西場景。
4. newScheduledThreadPool(int corePoolSize):創(chuàng)建一個(gè)具有指定線程數(shù)的線程池,并可以在指定延遲后執(zhí)行線程任務(wù),適用于多個(gè)后臺(tái)線程執(zhí)行周期任務(wù),同時(shí)為了滿足資源管理需要限制線程數(shù)量的場景。
實(shí)例代碼package com.wangjun.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /* * 演示線程池的使用 */ public class ThreadPoolTest { public static void main(String[] args) throws InterruptedException, ExecutionException { //生產(chǎn)普通的線程池 ExecutorService threadPool = Executors.newFixedThreadPool(6); // 第一種執(zhí)行線程的方式 threadPool.submit(new MyThread()); // 第二種執(zhí)行線程的方式,有返回值 Future3. 線程池類ThreadPoolExectorresult = threadPool.submit(new MyThread(), "返回值1"); System.out.println(result.get()); // 第三種執(zhí)行線程的方式,傳入Callable對(duì)象,有返回值 Future result2 = threadPool.submit(new MyThread2()); System.out.println(result2.get()); // 關(guān)閉線程池,不再接受新的任務(wù),會(huì)將之前所有提交的任務(wù)執(zhí)行完成,所有任務(wù)完成后,所有的線程死亡 // 調(diào)用shutdownNow可以立馬停止所有線程 threadPool.shutdown(); System.out.println("-----"); // 生產(chǎn)可以延遲執(zhí)行的線程池 ScheduledExecutorService threadPool2 = Executors.newScheduledThreadPool(6); // 延遲1秒執(zhí)行 threadPool2.schedule(new MyThread(), 1, TimeUnit.SECONDS); // 延遲2秒后執(zhí)行,每一秒循環(huán)執(zhí)行一次 // 是以上一個(gè)任務(wù)開始的時(shí)間計(jì)時(shí),period時(shí)間過去后,檢測(cè)上一個(gè)任務(wù)是否執(zhí)行完畢,如果上一個(gè)任務(wù)執(zhí)行完畢, // 則當(dāng)前任務(wù)立即執(zhí)行,如果上一個(gè)任務(wù)沒有執(zhí)行完畢,則需要等上一個(gè)任務(wù)執(zhí)行完畢后立即執(zhí)行。 threadPool2.scheduleAtFixedRate(new MyThread(), 2, 1, TimeUnit.SECONDS); // 是以上一個(gè)任務(wù)結(jié)束時(shí)開始計(jì)時(shí),period時(shí)間過去后,立即執(zhí)行。 // 兩個(gè)方法以不同的時(shí)間點(diǎn)作為參考 threadPool2.scheduleWithFixedDelay(new MyThread(), 2, 1, TimeUnit.SECONDS); Thread.sleep(3000); threadPool2.shutdown(); } static class MyThread extends Thread { @Override public void run() { System.out.println("線程:" + Thread.currentThread().getName()); } } static class MyThread2 implements Callable { @Override public String call() throws Exception { System.out.println("線程:" + Thread.currentThread().getName()); return "返回值2"; } } }
上面使用Executors工廠類生產(chǎn)線程池,其實(shí)大部分返回的就是ThreadPoolExector的實(shí)例。ThreadPoolExector才是真正的線程池。
3.1 線程池的幾個(gè)關(guān)鍵屬性核心線程數(shù)量corePoolSize:核心線程數(shù),指保留的線程池大?。ú怀^maximumPoolSize值時(shí),線程池中最多有corePoolSize 個(gè)線程工作)。
最大線程數(shù)量maximumPoolSize:指的是線程池的最大大小,使用使用了無界隊(duì)列,那這個(gè)參數(shù)就沒什么用。
線程結(jié)束的超時(shí)時(shí)間keepAliveTime:當(dāng)一個(gè)線程不工作時(shí),過keepAliveTime時(shí)間將停止該線程(該線程是多余的,指大于corePoolSize的那些線程)。
存放任務(wù)的隊(duì)列workQueue:存放需要被線程池執(zhí)行的線程隊(duì)列。
飽和策略handler:加任務(wù)失敗后如何處理該任務(wù)。通常是AbortPolicy,表示無法處理新任務(wù)時(shí)拋出異常。
3.2 線程池執(zhí)行策略線程池執(zhí)行execute()方法的過程如圖:
線程池剛創(chuàng)建時(shí),里面沒有一個(gè)線程。任務(wù)隊(duì)列是作為參數(shù)傳進(jìn)來的。不過,就算隊(duì)列里面有任務(wù),線程池也不會(huì)馬上執(zhí)行它們。
當(dāng)調(diào)用 execute() 方法添加一個(gè)任務(wù)時(shí),線程池會(huì)做如下判斷:
? a. 如果正在運(yùn)行的線程數(shù)量小于 corePoolSize,則創(chuàng)建線程執(zhí)行任務(wù)(注意,這一步需要獲取全局鎖);
? b. 如果正在運(yùn)行的線程數(shù)量大于或等于 corePoolSize并且隊(duì)列沒有滿,那么將這個(gè)任務(wù)放入隊(duì)列。
? c. 如果這時(shí)候隊(duì)列滿了,而且正在運(yùn)行的線程數(shù)量小于 maximumPoolSize,那么還是要?jiǎng)?chuàng)建線程運(yùn)行這個(gè)任務(wù);
? d. 如果隊(duì)列滿了,而且正在運(yùn)行的線程數(shù)量大于或等于 maximumPoolSize,那么線程池會(huì)交給配置的執(zhí)行策略處理(調(diào)用RejectedExecutionHandler.rejectedExecution方法),比如拋出異常,提示無法加入新線程。
當(dāng)一個(gè)線程完成任務(wù)時(shí),它會(huì)從隊(duì)列中取下一個(gè)任務(wù)來執(zhí)行。
當(dāng)一個(gè)線程無事可做,超過一定的時(shí)間(keepAliveTime)時(shí),線程池會(huì)判斷,如果當(dāng)前運(yùn)行 的線程數(shù)大于 corePoolSize,那么這個(gè)線程就被停掉。所以線程池的所有任務(wù)完成后,它最終會(huì)收縮到 corePoolSize 的大小。
3.3 線程池中阻塞隊(duì)列的種類ArrayBlockingQueue:居于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,按照先進(jìn)先出FIFO原則對(duì)元素進(jìn)行排序;
LinkedBlockingQueue:基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,按照FIFO排序原色,吞吐量大于ArrayBlockingQueue,靜態(tài)工廠方法Executors.newFixedThreadPool()就使用了這個(gè)隊(duì)列;
SynchronousQueue:不存儲(chǔ)元素的阻塞隊(duì)列。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工程方法Executors.newCachedThreadPool()就使用了這個(gè)隊(duì)列;
PriorityBlockingQueue:具有優(yōu)先級(jí)的無限阻塞隊(duì)列。
3.4 飽和策略一般情況下,線程池采用的是AbortPolicy,表示無法處理新任務(wù)時(shí)拋出異常。JDK1.5中java線程池框架提供了4種飽和策略:
AbortPolicy:直接拋出異常;
CallerRunsPolicy:使用調(diào)用者所在線程來運(yùn)行任務(wù);
DiscardOldestPolicy:丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù);
DiscardPolicy:不處理,丟棄掉。
也可以根據(jù)場景來實(shí)現(xiàn)RejectedExecutionHandler接口來自定義策略。
3.5 向線程池提交任務(wù)execute:用于提交不需要返回值的任務(wù),因此無法判斷任務(wù)是否被線程執(zhí)行成功;
submit:用于提交需要有返回值的任務(wù),返回一個(gè)Future對(duì)象,通過get()獲取返回值,會(huì)阻塞當(dāng)前線程直到任務(wù)完成。
3.6 關(guān)閉線程池通過調(diào)用線程池的shutdown和shutdownNow方法來關(guān)閉線程池,他們的原理是遍歷線程池中的工作線程,然后逐個(gè)調(diào)用線程的interrupt方法來中斷線程,所以無法影響中斷的任務(wù)可能永遠(yuǎn)無法終止。
這兩個(gè)方法的區(qū)別是shutdownNow首先將線程池的狀態(tài)設(shè)置為STOP,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待任務(wù)的列表,而shutdown只是將線程池的狀態(tài)設(shè)置為SHUTDOWN狀態(tài),然后中斷所有沒有正在執(zhí)行任務(wù)的線程。
只要調(diào)用了這兩個(gè)關(guān)閉方法中的任意一個(gè),isShutdown方法就會(huì)返回true,當(dāng)所有的任務(wù)都已關(guān)閉后,才表示線程池關(guān)閉成功,這時(shí)調(diào)用isTerminaed方法會(huì)返回true。通常調(diào)用shutdown來關(guān)閉線程池,如果任務(wù)不一定要執(zhí)行完,則可以調(diào)用shutdownNow方法。
4. Executor框架JDK5之前,java的線程Thread既是工作單元,也是執(zhí)行單元(start方法執(zhí)行),JDK5開始,把工作單元與執(zhí)行機(jī)制分離開來,工作單元包括Runnable和Callable,而執(zhí)行機(jī)制由Executor框架提供。(注意這里的Executor和Executors線程池工廠類不是一回事。)
線程池類ThreadPoolExecutor的繼承關(guān)系:
ThreadPoolExecutor -> AbstractExecutorService -> ExecutorService(接口) -> Executor(接口)。
可以看到ThreadPoolExecutor就是Executor接口的一個(gè)實(shí)現(xiàn)方法。
4.1 Executor的兩層調(diào)度模型傳統(tǒng)的java線程(java.lang.Thread)在JVM中被一對(duì)一映射為本地操作系統(tǒng)線程,java線程啟動(dòng)時(shí)會(huì)創(chuàng)建一個(gè)本地操作系統(tǒng)線程,當(dāng)java線程終止時(shí),這個(gè)操作系統(tǒng)線程也會(huì)被回收。
而在Executor框架中,將用戶任務(wù)映射為固定數(shù)量的線程,在底層操作系統(tǒng)內(nèi)核將這些線程映射到硬件處理器上,應(yīng)用程序通過Executor框架控制上層調(diào)度,下層的調(diào)度由操作系統(tǒng)內(nèi)核控制。
4.2 Executor框架的結(jié)構(gòu)Executor框架主要有3部分組成:
任務(wù):包括被執(zhí)行任務(wù)需要實(shí)現(xiàn)的接口:Runnable和Callable;
任務(wù)的執(zhí)行:核心接口是Executor,繼承Executor的ExecutorService接口。ExecutorService接口有兩個(gè)核心實(shí)現(xiàn)類,ThreadPoolExecutor和ScheduledThreadPoolExecutor(繼承ThreadPoolExecutor,可以延遲執(zhí)行任務(wù),比Timer更強(qiáng)大)。
異步計(jì)算的結(jié)果:包括接口Future和實(shí)現(xiàn)Future接口的FutureTask類。
來看一下Executor框架中主要類和接口的UML圖:
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69629.html
摘要:當(dāng)活動(dòng)線程核心線程非核心線程達(dá)到這個(gè)數(shù)值后,后續(xù)任務(wù)將會(huì)根據(jù)來進(jìn)行拒絕策略處理。線程池工作原則當(dāng)線程池中線程數(shù)量小于則創(chuàng)建線程,并處理請(qǐng)求。當(dāng)線程池中的數(shù)量等于最大線程數(shù)時(shí)默默丟棄不能執(zhí)行的新加任務(wù),不報(bào)任何異常。 spring-cache使用記錄 spring-cache的使用記錄,坑點(diǎn)記錄以及采用的解決方案 深入分析 java 線程池的實(shí)現(xiàn)原理 在這篇文章中,作者有條不紊的將 ja...
摘要:四種線程池的使用介紹的弊端及四種線程池的使用,線程池的作用線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。相比,提供的四種線程池的好處在于重用存在的線程,減少對(duì)象創(chuàng)建消亡的開銷,性能佳。延遲執(zhí)行描述創(chuàng)建一個(gè)定長線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。 java 四種線程池的使用 介紹new Thread的弊端及Java四種線程池的使用 1,線程池的作用 線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。 ...
系統(tǒng)啟動(dòng)一個(gè)線程的成本是比較高,使用線程池可以很好地提高性能,尤其是當(dāng)程序中需要?jiǎng)?chuàng)建大量生存期很短暫的線程時(shí) 線程池在系統(tǒng)啟動(dòng)時(shí)即創(chuàng)建大量空閑線程,將一個(gè)Runnable、Callable對(duì)象—–>傳給線程池—–>線程池啟動(dòng)里面的一個(gè)線程來執(zhí)行它們的run()或者call()方法———->當(dāng)線程執(zhí)行體執(zhí)行完成后,線程并不會(huì)死亡,而是再次返回線程池成為空閑狀態(tài),等待下一個(gè)Runnable、Calla...
摘要:高并發(fā)系列第篇文章。簡單的說,在使用了線程池之后,創(chuàng)建線程變成了從線程池中獲取一個(gè)空閑的線程,然后使用,關(guān)閉線程變成了將線程歸還到線程池。如果調(diào)用了線程池的方法,線程池會(huì)提前把核心線程都創(chuàng)造好,并啟動(dòng)線程池允許創(chuàng)建的最大線程數(shù)。 java高并發(fā)系列第18篇文章。 本文主要內(nèi)容 什么是線程池 線程池實(shí)現(xiàn)原理 線程池中常見的各種隊(duì)列 自定義線程創(chuàng)建的工廠 常見的飽和策略 自定義飽和策略 ...
摘要:中的線程池運(yùn)用場景非常廣泛,幾乎所有的一步或者并發(fā)執(zhí)行程序都可以使用。代碼中如果執(zhí)行了方法,線程池會(huì)提前創(chuàng)建并啟動(dòng)所有核心線程。線程池最大數(shù)量線程池允許創(chuàng)建的線程最大數(shù)量。被稱為是可重用固定線程數(shù)的線程池。 Java中的線程池運(yùn)用場景非常廣泛,幾乎所有的一步或者并發(fā)執(zhí)行程序都可以使用。那么線程池有什么好處呢,以及他的實(shí)現(xiàn)原理是怎么樣的呢? 使用線程池的好處 在開發(fā)過程中,合理的使用線程...
閱讀 3164·2021-11-19 09:40
閱讀 1590·2021-11-15 11:39
閱讀 704·2021-10-08 10:05
閱讀 2305·2021-09-03 10:29
閱讀 3437·2021-08-12 13:22
閱讀 2238·2019-08-30 15:54
閱讀 3745·2019-08-30 14:03
閱讀 2673·2019-08-30 13:45