標籤:size cat 解釋 就會 ack 規則 阻塞 ali workqueue
為什麼用線程池?
- 1.建立/銷毀線程伴隨著系統開銷,過於頻繁的建立/銷毀線程,會很大程度上影響處-理效率
- 2.線程並發數量過多,搶佔系統資源從而導致阻塞
- 3.對線程進行一些簡單的管理
在Java中,線程池的概念是Executor這個介面,具體實現為ThreadPoolExecutor類,學習Java中的線程池,就可以直接學習他了對線程池的配置,就是對ThreadPoolExecutor建構函式的參數的配置
一、ThreadPoolExecutor提供了四個建構函式:
//五個參數的建構函式public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)//六個參數的建構函式-1public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)//六個參數的建構函式-2public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)//七個參數的建構函式public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
下面來解釋下各個參數:
- int corePoolSize:該線程池中核心線程數最大值
核心線程:線程池建立線程的時候,如果當前線程總數小於corePoolSize,則建立的是核心線程,如果超過corePoolSize,則建立的是非核心線程核心線程預設情況下會一直存活線上程池中,即使這個核心線程啥也不幹(閑置狀態)。
如果指定ThreadPoolExecutor的allowCoreThreadTimeOut這個屬性為true,那麼核心線程如果不幹活(閑置狀態)的話,超過一定時間(時間長度下面參數決定),就會被銷毀掉。
- int maximumPoolSize: 該線程池中線程總數最大值
線程總數 = 核心線程數 + 非核心線程數。
- long keepAliveTime:該線程池中非核心線程閑置逾時時間長度
一個非核心線程,如果不幹活(閑置狀態)的時間長度超過這個參數所設定的時間長度,就會被銷毀掉,如果設定allowCoreThreadTimeOut = true,則會作用於核心線程。
- TimeUnit unit:keepAliveTime的單位
TimeUnit是一個枚舉類型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小時
DAYS : 天
- BlockingQueue workQueue:該線程池中的任務隊列:維護著等待執行的Runnable對象
當所有的核心線程都在幹活時,新添加的任務會被添加到這個隊列中等待處理,如果隊列滿了,則建立非核心線程執行任務。
常用的workQueue類型:
SynchronousQueue:這個隊列接收到任務的時候,會直接提交給線程處理,而不保留它,如果所有線程都在工作怎麼辦?那就建立一個線程來處理這個任務!所以為了保證不出現<線程數達到了maximumPoolSize而不能建立線程>的錯誤,使用這個類型隊列的時候,maximumPoolSize一般指定成Integer.MAX_VALUE,即無限大
LinkedBlockingQueue:這個隊列接收到任務的時候,如果當前線程數小於核心線程數,則建立線程(核心線程)處理任務;如果當前線程數等於核心線程數,則進入隊列等待。由於這個隊列沒有最大值限制,即所有超過核心線程數的任務都將被添加到隊列中,這也就導致了maximumPoolSize的設定失效,因為匯流排程數永遠不會超過corePoolSize
ArrayBlockingQueue:可以限定隊列的長度,接收到任務的時候,如果沒有達到corePoolSize的值,則建立線程(核心線程)執行任務,如果達到了,則入隊等候,如果隊列已滿,則建立線程(非核心線程)執行任務,又如果匯流排程數到了maximumPoolSize,並且隊列也滿了,則發生錯誤
DelayQueue:隊列內元素必須實現Delayed介面,這就意味著你傳進去的任務必須先實現Delayed介面。這個隊列接收到任務時,首先先入隊,只有達到了指定的延時時間,才會執行任務
ThreadFactory threadFactory:建立線程的方式,這是一個介面,你new他的時候需要實現他的Thread newThread(Runnable r)方法,一般用不上。
RejectedExecutionHandler handler:這玩意兒就是拋出異常專用的,比如上面提到的兩個錯誤發生了,就會由這個handler拋出異常,根本用不上。
二、向ThreadPoolExecutor新增工作
我們怎麼知道new一個ThreadPoolExecutor,大概知道各個參數是幹嘛的,可是我new完了,怎麼向線程池提交一個要執行的任務啊?
ThreadPoolExecutor.execute(Runnable command)
通過ThreadPoolExecutor.execute(Runnable command)方法即可向線程池內添加一個任務。
三、ThreadPoolExecutor的策略
這裡給總結一下,當一個任務被添加進線程池時,執行策略:
- 1.線程數量未達到corePoolSize,則建立一個線程(核心線程)執行任務
- 2.線程數量達到了corePools,則將任務移入隊列等待
- 3.隊列已滿,建立線程(非核心線程)執行任務
- 4.隊列已滿,匯流排程數又達到了maximumPoolSize,就會由(RejectedExecutionHandler)拋出異常
+++++++++++++++++++++++++++我是分割線++++++++++++++++++++++++++++
常見四種線程池:
如果你不想自己寫一個線程池,Java通過Executors提供了四種線程池,這四種線程池都是直接或間接配置ThreadPoolExecutor的參數實現的。
1.可緩衝線程池CachedThreadPool()
源碼:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
根據源碼可以看出:
- 這種線程池內部沒有核心線程,線程的數量是有沒限制的。
- 在建立任務時,若有閒置線程時則複用閒置線程,若沒有則建立線程。
- 沒有工作的線程(閑置狀態)在超過了60S還不做事,就會銷毀。
建立方法:
ExecutorService mCachedThreadPool = Executors.newCachedThreadPool();
用法:
//開始下載private void startDownload(final ProgressBar progressBar, final int i) { mCachedThreadPool.execute(new Runnable() { @Override public void run() { int p = 0; progressBar.setMax(10);//每個下載任務10秒 while (p < 10) { p++; progressBar.setProgress(p); Bundle bundle = new Bundle(); Message message = new Message(); bundle.putInt("p", p); //把當前線程的名字用handler讓textview顯示出來 bundle.putString("ThreadName", Thread.currentThread().getName()); message.what = i; message.setData(bundle); mHandler.sendMessage(message); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); }
2.FixedThreadPool 定長線程池
源碼:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());}
根據源碼可以看出:
- 該線程池的最大線程數等於核心線程數,所以在預設情況下,該線程池的線程不會因為閑置狀態逾時而被銷毀。
- 如果當前線程數小於核心線程數,並且也有閑置線程的時候提交了任務,這時也不會去複用之前的閑置線程,會建立新的線程去執行任務。如果當前執行任務數大於了核心線程數,大於的部分就會進入隊列等待。等著有閑置的線程來執行這個任務。
建立方法:
//nThreads => 最大線程數即maximumPoolSizeExecutorService mFixedThreadPool= Executors.newFixedThreadPool(int nThreads);//threadFactory => 建立線程的方法,用得少ExecutorService mFixedThreadPool= Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
用法:
private void startDownload(final ProgressBar progressBar, final int i) { mFixedThreadPool.execute(new Runnable() { @Override public void run() { //....邏輯代碼自己控制 } }); }
3.SingleThreadPool
源碼:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));}
根據源碼可以看出:
- 有且僅有一個背景工作執行緒執行任務
- 所有任務按照指定順序執行,即遵循隊列的入隊出隊規則
建立方法:
ExecutorService mSingleThreadPool = Executors.newSingleThreadPool();
用法同上。
4.ScheduledThreadPool
源碼:
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());}
根據源碼可以看出:
DEFAULT_KEEPALIVE_MILLIS就是預設10L,這裡就是10秒。這個線程池有點像是吧CachedThreadPool和FixedThreadPool 結合了一下。
- 不僅設定了核心線程數,最大線程數也是Integer.MAX_VALUE。
- 這個線程池是上述4個中為唯一個有順延強制和周期執行任務的線程池。
建立:
//nThreads => 最大線程數即maximumPoolSizeExecutorService mScheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
一般的執行任務方法和上面的都大同小異,我們主要看看延時執行任務和周期執行任務的方法。
//表示在3秒之後開始執行我們的任務。mScheduledThreadPool.schedule(new Runnable() { @Override public void run() { //.... } }, 3, TimeUnit.SECONDS);
//延遲3秒後執行任務,從開始執行任務這個時候開始計時,每7秒執行一次不管執行任務需要多長的時間。 mScheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { //.... } },3, 7, TimeUnit.SECONDS);
/**延遲3秒後執行任務,從任務完成時這個時候開始計時,7秒後再執行,*再等完成後計時7秒再執行也就是說這裡的迴圈執行任務的時間點是*從上一個任務完成的時候。*/mScheduledThreadPool.scheduleWithFixedDelay(new Runnable() { @Override public void run() { //.... } },3, 7, TimeUnit.SECONDS);
以上就是常用的四個線程池以及他們的實現原理。
我弟是個程式員
連結:https://www.jianshu.com/p/ae67972d1156
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。
Java常用四大線程池用法以及ThreadPoolExecutor詳解