摘要:創建方法最大線程數即源碼單線程化的線程池有且僅有一個工作線程執行任務所有任務按照指定順序執行,即遵循隊列的入隊出隊規則創建方法源碼還有一個結合了和,就不介紹了,基本不用。
*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發布
為什么用線程池
創建/銷毀線程伴隨著系統開銷,過于頻繁的創建/銷毀線程,會很大程度上影響處理效率
>例如: > >記創建線程消耗時間T1,執行任務消耗時間T2,銷毀線程消耗時間T3 > >如果T1+T3>T2,那么是不是說開啟一個線程來執行這個任務太不劃算了! > >正好,線程池緩存線程,可用已有的閑置線程來執行新任務,避免了T1+T3帶來的系統開銷
線程并發數量過多,搶占系統資源從而導致阻塞
>我們知道線程能共享系統資源,如果同時執行的線程過多,就有可能導致系統資源不足而產生阻塞的情況 > >運用線程池能有效的控制線程最大并發數,避免以上的問題
對線程進行一些簡單的管理
> 比如:延時執行、定時循環執行的策略等 > > 運用線程池都能進行很好的實現線程池ThreadPoolExecutor
既然Android中線程池來自于Java,那么研究Android線程池其實也可以說是研究Java中的線程池
在Java中,線程池的概念是Executor這個接口,具體實現為ThreadPoolExecutor類,學習Java中的線程池,就可以直接學習他了
對線程池的配置,就是對ThreadPoolExecutor構造函數的參數的配置,既然這些參數這么重要,就來看看構造函數的各個參數吧
ThreadPoolExecutor提供了四個構造函數//五個參數的構造函數 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue) //六個參數的構造函數-1 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) //六個參數的構造函數-2 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) //七個參數的構造函數 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
我知道你看到這些構造函數和我一樣也是嚇呆了,但其實一共就7種類型,理解起來簡直和理解一周有7天一樣簡單,而且一周有兩天是周末,其實也就只有5天需要了解!相信我,畢竟扯皮,我比較擅長
int corePoolSize => 該線程池中核心線程數最大值
> **核心線程:** > > 線程池新建線程的時候,如果當前線程總數小于corePoolSize,則新建的是核心線程,如果超過corePoolSize,則新建的是非核心線程 > > 核心線程默認情況下會一直存活在線程池中,即使這個核心線程啥也不干(閑置狀態)。 > > 如果指定ThreadPoolExecutor的allowCoreThreadTimeOut這個屬性為true,那么核心線程如果不干活(閑置狀態)的話,超過一定時間(時長下面參數決定),就會被銷毀掉 > > 很好理解吧,正常情況下你不干活我也養你,因為我總有用到你的時候,但有時候特殊情況(比如我自己都養不起了),那你不干活我就要把你干掉了
int maximumPoolSize
> 該線程池中**線程總數最大值** > > 線程總數 = 核心線程數 + 非核心線程數。核心線程在上面解釋過了,這里說下非核心線程: > > 不是核心線程的線程(別激動,把刀放下...),其實在上面解釋過了
long keepAliveTime
> 該線程池中**非核心線程閑置超時時長** > > 一個非核心線程,如果不干活(閑置狀態)的時長超過這個參數所設定的時長,就會被銷毀掉 > > 如果設置allowCoreThreadTimeOut = true,則會作用于核心線程
TimeUnit unit
> keepAliveTime的單位,TimeUnit是一個枚舉類型,其包括: > > 1. NANOSECONDS : 1微毫秒 = 1微秒 / 1000 > 2. MICROSECONDS : 1微秒 = 1毫秒 / 1000 > 3. MILLISECONDS : 1毫秒 = 1秒 /1000 > 4. SECONDS : 秒 > 5. MINUTES : 分 > 6. HOURS : 小時 > 7. DAYS : 天
BlockingQueue
> 該線程池中的任務隊列:維護著等待執行的Runnable對象 > > 當所有的核心線程都在干活時,新添加的任務會被添加到這個隊列中等待處理,如果隊列滿了,則新建非核心線程執行任務 > > 常用的workQueue類型: > > 1. **SynchronousQueue:**這個隊列接收到任務的時候,會直接提交給線程處理,而不保留它如果所有線程都在工作怎么辦?那就新建一個線程來處理這個任務!所以為了保證不出現<線程數達到了maximumPoolSize而不能新建線程>的錯誤,使用這個類型隊列的時候,maximumPoolSize一般指定成Integer.MAX_VALUE,即無限大 > > 2. **LinkedBlockingQueue:**這個隊列接收到任務的時候,如果當前線程數小于核心線程數,則新建線程(核心線程)處理任務;如果當前線程數等于核心線程數,則進入隊列等待。由于這個隊列沒有最大值限制,即所有超過核心線程數的任務都將被添加到隊列中,這也就導致了maximumPoolSize的設定失效,因為總線程數永遠不會超過corePoolSize > > 3. **ArrayBlockingQueue:**可以限定隊列的長度,接收到任務的時候,如果沒有達到corePoolSize的值,則新建線程(核心線程)執行任務,如果達到了,則入隊等候,如果隊列已滿,則新建線程(非核心線程)執行任務,又如果總線程數到了maximumPoolSize,并且隊列也滿了,則發生錯誤 > > 4. **DelayQueue:**隊列內元素必須實現Delayed接口,這就意味著你傳進去的任務必須先實現Delayed接口。這個隊列接收到任務時,首先先入隊,只有達到了指定的延時時間,才會執行任務
ThreadFactory threadFactory
> 創建線程的方式,這是一個接口,你new他的時候需要實現他的`Thread newThread(Runnable r)`方法,一般用不上,**這是星期六,休息** > > 但我還是說一句吧(把槍放下...) > > 小伙伴應該知道AsyncTask是對線程池的封裝吧?那就直接放一個AsyncTask新建線程池的threadFactory參數源碼吧: > > ``` > new ThreadFactory() { > private final AtomicInteger mCount = new AtomicInteger(1); > > public Thread new Thread(Runnable r) { > return new Thread(r,"AsyncTask #" + mCount.getAndIncrement()); > } > } > ``` > 這么簡單?就給線程起了個名?!對啊,所以說這是星期六啊,別管他了,雖然我已經強迫你們看完了...
RejectedExecutionHandler handler
> 這玩意兒就是拋出異常專用的,比如上面提到的兩個錯誤發生了,就會由這個handler拋出異常,你不指定他也有個默認的 > > 拋異常能拋出什么花樣來?所以這個星期天不管了,一邊去,根本用不上
新建一個線程池的時候,一般只用5個參數的構造函數。
向ThreadPoolExecutor添加任務那說了這么多,你可能有疑惑,我知道new一個ThreadPoolExecutor,大概知道各個參數是干嘛的,可是我new完了,怎么向線程池提交一個要執行的任務啊?
通過ThreadPoolExecutor.execute(Runnable command)方法即可向線程池內添加一個任務
ThreadPoolExecutor的策略上面介紹參數的時候其實已經說到了ThreadPoolExecutor執行的策略,這里給總結一下,當一個任務被添加進線程池時:
線程數量未達到corePoolSize,則新建一個線程(核心線程)執行任務
線程數量達到了corePools,則將任務移入隊列等待
隊列已滿,新建線程(非核心線程)執行任務
隊列已滿,總線程數又達到了maximumPoolSize,就會由上面那位星期天(RejectedExecutionHandler)拋出異常
常見四種線程池如果你不想自己寫一個線程池,那么你可以從下面看看有沒有符合你要求的(一般都夠用了),如果有,那么很好你直接用就行了,如果沒有,那你就老老實實自己去寫一個吧
Java通過Executors提供了四種線程池,這四種線程池都是直接或間接配置ThreadPoolExecutor的參數實現的,下面我都會貼出這四種線程池構造函數的源碼,各位大佬們一看便知!
來,走起:
CachedThreadPool()可緩存線程池:
線程數無限制
有空閑線程則復用空閑線程,若無空閑線程則新建線程
一定程序減少頻繁創建/銷毀線程,減少系統開銷
創建方法:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源碼:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
通過我上面行云流水談笑風生天馬行空滔滔不絕的對各種參數的說明,這個源碼你肯定一眼就看懂了,想都不用想(下面三種一樣啦)
FixedThreadPool()定長線程池:
可控制線程最大并發數(同時執行的線程數)
超出的線程會在隊列中等待
創建方法:
//nThreads => 最大線程數即maximumPoolSize ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads); //threadFactory => 創建線程的方法,這就是我叫你別理他的那個星期六!你還看! ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
源碼:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
2個參數的構造方法源碼,不用我貼你也知道他把星期六放在了哪個位置!所以我就不貼了,省下篇幅給我扯皮
ScheduledThreadPool()定長線程池:
支持定時及周期性任務執行。
創建方法:
//nThreads => 最大線程數即maximumPoolSize ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
源碼:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } //ScheduledThreadPoolExecutor(): public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }SingleThreadExecutor()
單線程化的線程池:
有且僅有一個工作線程執行任務
所有任務按照指定順序執行,即遵循隊列的入隊出隊規則
創建方法:
ExecutorService singleThreadPool = Executors.newSingleThreadPool();
源碼:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
還有一個Executors.newSingleThreadScheduledExecutor()結合了3和4,就不介紹了,基本不用。
結語墻裂建議各位看完本文一定要實際動手去敲一遍都驗證一遍,這樣才能很好的掌握知識
動手做,永遠是學習的最好的方式!
end
更多內容歡迎訪問我的主頁或我的博客
如果我的文章確實有幫助到你,請不要忘了點一下文末的"?"讓他變成"?"
作為小菜鳥難免很多地方理解不到位,文中若有錯誤請直(bu)接(yao)指(ma)出(wo)
寫作不易!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67049.html
摘要:也是自帶的一個基于線程池設計的定時任務類。其每個調度任務都會分配到線程池中的一個線程執行,所以其任務是并發執行的,互不影響。 原創不易,如需轉載,請注明出處https://www.cnblogs.com/baixianlong/p/10659045.html,否則將追究法律責任!!! 一、在JAVA開發領域,目前可以通過以下幾種方式進行定時任務 1、單機部署模式 Timer:jdk中...
摘要:高并發系列第篇文章。簡單的說,在使用了線程池之后,創建線程變成了從線程池中獲取一個空閑的線程,然后使用,關閉線程變成了將線程歸還到線程池。如果調用了線程池的方法,線程池會提前把核心線程都創造好,并啟動線程池允許創建的最大線程數。 java高并發系列第18篇文章。 本文主要內容 什么是線程池 線程池實現原理 線程池中常見的各種隊列 自定義線程創建的工廠 常見的飽和策略 自定義飽和策略 ...
摘要:前言從號開始在寫下第一篇文章說是筆記還差不多,驚奇地收到有人收藏我的文章的消息,覺得有點開心。突然腦子抽到想爬下里標簽下的文章有多少,哪篇被收藏最多,哪篇被點贊最多。。。現在和大家分享下,收藏量前的文章,被那么多人收藏應該是篇值得看的文章。 前言 從18號開始在sf寫下第一篇文章(說是筆記還差不多),驚奇地收到有人收藏我的文章的消息,覺得有點開心。突然腦子抽到想爬下sf里JAVA標簽下...
摘要:如果什么事都沒得做,它也不會死循環,它會將線程休眠起來,直到下一個事件來了再繼續干活,這樣的一個線程稱之為線程。而請求處理邏輯既可以使用單獨的線程池進行處理,也可以跟放在讀寫線程一塊處理。 Netty到底是什么 從HTTP說起 有了Netty,你可以實現自己的HTTP服務器,FTP服務器,UDP服務器,RPC服務器,WebSocket服務器,Redis的Proxy服務器,MySQL的P...
閱讀 882·2021-11-23 09:51
閱讀 1088·2021-11-15 17:57
閱讀 1667·2021-09-22 15:24
閱讀 812·2021-09-07 09:59
閱讀 2221·2019-08-29 15:10
閱讀 1849·2019-08-29 12:47
閱讀 751·2019-08-29 12:30
閱讀 3369·2019-08-26 13:51