您好,登錄后才能下訂單哦!
不懂JAVA線程池的作用是什么??其實(shí)想解決這個(gè)問題也不難,下面讓小編帶著大家一起學(xué)習(xí)怎么去解決,希望大家閱讀完這篇文章后大所收獲。
線程池的作用
我們?cè)谟靡粋€(gè)東西的時(shí)候,首先得搞明白一個(gè)問題。這玩意是干嘛的,為啥要用這個(gè),用別的不行嗎。那么一個(gè)一個(gè)解決這些問題
我們之前都用過數(shù)據(jù)庫連接池,線程池的作用和連接池有點(diǎn)類似,頻繁的創(chuàng)建,銷毀線程會(huì)造成大量的不必要的性能開銷,所以這個(gè)時(shí)候就出現(xiàn)了一個(gè)東西統(tǒng)一的管理線程,去負(fù)責(zé)線程啥時(shí)候銷毀,啥時(shí)候創(chuàng)建,以及維持線程的狀態(tài),當(dāng)程序需要使用線程的時(shí)候,直接從線程池拿,當(dāng)程序用完了之后,直接把線程放回線程池,不需要去管線程的生命周期,專心的執(zhí)行業(yè)務(wù)代碼就行。
當(dāng)然,如果非要是自己想手動(dòng)new一個(gè)線程來執(zhí)行,也不是不可以,只是像上面說的那樣,第一麻煩,第二開銷大,第三不好控制。
控制線程的方法
在說到線程池之前,首先要提到一個(gè)創(chuàng)建線程池的工具類,又或者說是工廠類 Executors 通過這個(gè)線程可以統(tǒng)一的創(chuàng)建線程,返回的是一個(gè)ExecutorService 類這個(gè)類中包含了一些對(duì)線程執(zhí)行過程進(jìn)行管理控制的方法;
void execute(Runnable command);
這個(gè)方法是將任務(wù)提交到線程池進(jìn)行執(zhí)行。這個(gè)方法沒有返回值。
<T> Future<T> submit(Callable<T> task);
這個(gè)方法最特別的地方是線程執(zhí)行完畢之后是有返回值的,另外方法的參數(shù)可以用Callable也可以為Runnable??梢赃m用于一些后續(xù)的代碼,需要線程執(zhí)行結(jié)果的程序。
下面的示例中,我們創(chuàng)建了一個(gè) ExecutorService 的實(shí)例,提交了一個(gè)任務(wù),然后使用返回的 Future 的 get() 方法等待提交的任務(wù)完成并返回值。
ExecutorService executorService = Executors.newFixedThreadPool(10); Future<String> future = executorService.submit(() -> "Hello World"); // 一些其它操作 String result = future.get();
在實(shí)際使用時(shí),我們并不會(huì)立即調(diào)用 future.get()
方法,可能會(huì)等待一些時(shí)間,推遲調(diào)用它直到我們需要它的值用于計(jì)算等目的。
ExecutorService
中的 submit()
方法被重載為支持 Runnable
或 Callable
,它們都是功能接口,可以接收一個(gè) lambdas 作為參數(shù)( 從 Java 8 開始 ):
如果想讓編譯器將參數(shù)推斷為 Callable 類型,只需要 lambda 返回一個(gè)值即可。
void shutdown();
在調(diào)用了shutdown方法之后,線程池就不會(huì)再接收新的任務(wù),此時(shí)線程池還沒有停止,仍然會(huì)把線程池中國正在執(zhí)行但是還沒有執(zhí)行完的任務(wù)繼續(xù)執(zhí)行完畢,那些沒有開始執(zhí)行的任務(wù)則被中斷List<Runnable> shutdownNow();
在調(diào)用了shutdownNow方法之后,會(huì)將線程池的狀態(tài)設(shè)置為stop,正在執(zhí)行的任務(wù)則被停止,沒被執(zhí)行任務(wù)的則返回。這兩種方法的使用場(chǎng)景:如果線程中的任務(wù)相互之間沒有什么關(guān)聯(lián)某個(gè)線程的異常對(duì)結(jié)果影響不大。那么所有線程都能在執(zhí)行任務(wù)結(jié)束之后可以正常結(jié)束,程序能在所有task都做完之后正常退出,適合用ShutDown。但是,如果一個(gè)線程在做某個(gè)任務(wù)的時(shí)候失敗,則整個(gè)結(jié)果就是失敗的,其他worker再繼續(xù)做剩下的任務(wù)也是徒勞,這就需要讓他們?nèi)客V巩?dāng)前的工作。這里使用ShutDownNow就可以讓該pool中的所有線程都停止當(dāng)前的工作,從而迫使所有線程執(zhí)行退出。從而讓主程序正常退出。
線程池的分類
通過工廠類 Executors
通過這個(gè)線程可以根據(jù)自己的需要統(tǒng)一的創(chuàng)建各種類型的線程,線程的分類大致分為以下四種:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
public class SinglePoolDemo { public static void main(String[] args) { ExecutorService pool = Executors.newSingleThreadExecutor(); // ExecutorService pool = Executors.newFixedThreadPool(2); for (int i = 0; i < 10; i++) { int finalI = i; pool.execute(() -> { System.out.println(Thread.currentThread().getName()+"----"+ finalI); }); } } }
輸出結(jié)果:
pool-1-thread-1----0
pool-1-thread-1----1
pool-1-thread-1----2
pool-1-thread-1----3
pool-1-thread-1----4
pool-1-thread-1----5
pool-1-thread-1----6
pool-1-thread-1----7
pool-1-thread-1----8
pool-1-thread-1----9
觀察線程編號(hào),可以發(fā)現(xiàn),自始自終都只有一個(gè)線程在執(zhí)行,并且也是按照順序來執(zhí)行的,。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
public class CachePoolDemo { public static void main(String[] args) { ExecutorService pool = Executors.newCachedThreadPool(); for (int i = 0; i < 20000; i++) { int finalI = i; pool.submit(() -> { System.out.println(Thread.currentThread().getName()+"-------------"+finalI); }); } } }
運(yùn)行結(jié)果部分:
......
pool-1-thread-1805-------------19760
pool-1-thread-1806-------------19783
pool-1-thread-1809-------------19875
pool-1-thread-1810-------------19951
pool-1-thread-1811-------------19980
以上的代碼我們運(yùn)行了2w次線程任務(wù),如果是按照我們之前的做法的話,我們要new 2w的線程去執(zhí)行。通過這個(gè)不定長(zhǎng)的線程池,他可以根據(jù)任務(wù)數(shù)來靈活的分配所創(chuàng)建的線程,如果線程池長(zhǎng)度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程,所以這里只創(chuàng)建了大概1800多個(gè)線程就完成了我們?cè)拘枰猲ew 2w個(gè)線程才能完成的任務(wù),之所以說他是靈活分配的是因?yàn)?,可以這樣驗(yàn)證看看,把i的值改為20的話,所創(chuàng)建的線程數(shù)量大概是10以內(nèi),因此是根據(jù)任務(wù)數(shù)量來自行創(chuàng)建線程數(shù)的,可以保證效率和性能的最大化。
但是經(jīng)過實(shí)測(cè),這個(gè)靈活性雖然最高,但是性能貌似是相對(duì)比較差的,在兩萬任務(wù)數(shù)的條件下,所以他的缺點(diǎn)就是,可能會(huì)創(chuàng)建大量的線程。當(dāng)然線程池這東西是需要根據(jù)自身情況來選擇的。如果主線程提交任務(wù)的速度遠(yuǎn)遠(yuǎn)大于CachedThreadPool的處理速度,則CachedThreadPool會(huì)不斷地創(chuàng)建新線程來執(zhí)行任務(wù),這樣有可能會(huì)導(dǎo)致系統(tǒng)耗盡CPU和內(nèi)存資源,所以在使用該線程池是,一定要注意控制并發(fā)的任務(wù)數(shù),否則創(chuàng)建大量的線程可能導(dǎo)致嚴(yán)重的性能問題。
newFixedThreadPool
可以通過傳入一個(gè)int參數(shù)來指定創(chuàng)建一個(gè)定長(zhǎng)的線程池,該線程池的核心線程數(shù)和最大線程數(shù)都是你傳進(jìn)去的參數(shù)的值,存活時(shí)間都為0說明只要任務(wù)空閑下來了,就會(huì)被銷毀,阻塞隊(duì)列的最大值為MAX_VALUE。所以他的缺點(diǎn)是,可能會(huì)將大量的時(shí)間花在處理堆積的請(qǐng)求阻塞隊(duì)列中的線程。public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
public class FixedPoolDemo { public static void main(String[] args) { ExecutorService pool = Executors.newFixedThreadPool(10); // ExecutorService pool = Executors.newFixedThreadPool(2); for (int i = 0; i < 1000; i++) { int finalI = i; pool.execute(() -> { System.out.println(Thread.currentThread().getName()+"----"+ finalI); }); } } }
從運(yùn)行結(jié)果可以看出,線程池一直都是維持著十個(gè)線程
.....
pool-1-thread-5----882
pool-1-thread-1----881
pool-1-thread-4----865
pool-1-thread-10----989
pool-1-thread-3----931
pool-1-thread-2----934
pool-1-thread-9----910
pool-1-thread-6----896
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
以上四種線程池,各有優(yōu)劣點(diǎn)
newFixedThreadPool、newSingleThreadExecutor:
主要問題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至OOM。
newCachedThreadPool、newScheduledThreadPool:
主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE,可能會(huì)創(chuàng)建數(shù)量非常多的線程,甚至OOM。
阿里線程池規(guī)范
總結(jié)
本篇文章首先我們知道了線程池有什么好處,然后了解一些線程的執(zhí)行方法,submit,execute,shutdown以及他們的區(qū)別,用法等等,然后對(duì)幾種線程池做了一個(gè)大概的介紹,以及他們的作用,好處和弊端。如果看的細(xì)心的同學(xué)可以看代碼發(fā)現(xiàn),這些線程池其實(shí)本質(zhì)上都是通過創(chuàng)建一個(gè) ThreadPoolExecutor
,包括阿里的線程池規(guī)范也是建議用ThreadPoolExecutor
,但是本篇文章只是對(duì)線程池的作用以及分類做一個(gè)概述,在下篇文章中,將會(huì)詳細(xì)的講一下ThreadPoolExecutor
。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享JAVA線程池的作用是什么??jī)?nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細(xì)的解決方法等著你來學(xué)習(xí)!
免責(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)容。