爲何用線程池?程序員
在Java中,線程池的概念是Executor這個接口,具體實現爲ThreadPoolExecutor類,學習Java中的線程池,就能夠直接學習他了對線程池的配置,就是對ThreadPoolExecutor構造函數的參數的配置緩存
//五個參數的構造函數 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) //六個參數的構造函數-1 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) //六個參數的構造函數-2 public 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)
下面來解釋下各個參數:併發
核心線程:線程池新建線程的時候,若是當前線程總數小於corePoolSize,則新建的是核心線程,若是超過corePoolSize,則新建的是非核心線程核心線程默認狀況下會一直存活在線程池中,即便這個核心線程啥也不幹(閒置狀態)。
若是指定ThreadPoolExecutor的allowCoreThreadTimeOut這個屬性爲true,那麼核心線程若是不幹活(閒置狀態)的話,超過必定時間(時長下面參數決定),就會被銷燬掉。ide
線程總數 = 核心線程數 + 非核心線程數。函數
一個非核心線程,若是不幹活(閒置狀態)的時長超過這個參數所設定的時長,就會被銷燬掉,若是設置allowCoreThreadTimeOut = true,則會做用於核心線程。學習
TimeUnit是一個枚舉類型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小時
DAYS : 天spa
當全部的核心線程都在幹活時,新添加的任務會被添加到這個隊列中等待處理,若是隊列滿了,則新建非核心線程執行任務。
經常使用的workQueue類型:線程
SynchronousQueue:這個隊列接收到任務的時候,會直接提交給線程處理,而不保留它,若是全部線程都在工做怎麼辦?那就新建一個線程來處理這個任務!因此爲了保證不出現<線程數達到了maximumPoolSize而不能新建線程>的錯誤,使用這個類型隊列的時候,maximumPoolSize通常指定成Integer.MAX_VALUE,即無限大code
LinkedBlockingQueue:這個隊列接收到任務的時候,若是當前線程數小於核心線程數,則新建線程(核心線程)處理任務;若是當前線程數等於核心線程數,則進入隊列等待。因爲這個隊列沒有最大值限制,即全部超過核心線程數的任務都將被添加到隊列中,這也就致使了maximumPoolSize的設定失效,由於總線程數永遠不會超過corePoolSize對象
ArrayBlockingQueue:能夠限定隊列的長度,接收到任務的時候,若是沒有達到corePoolSize的值,則新建線程(核心線程)執行任務,若是達到了,則入隊等候,若是隊列已滿,則新建線程(非核心線程)執行任務,又若是總線程數到了maximumPoolSize,而且隊列也滿了,則發生錯誤
DelayQueue:隊列內元素必須實現Delayed接口,這就意味着你傳進去的任務必須先實現Delayed接口。這個隊列接收到任務時,首先先入隊,只有達到了指定的延時時間,纔會執行任務
ThreadFactory threadFactory:建立線程的方式,這是一個接口,你new他的時候須要實現他的Thread newThread(Runnable r)方法,通常用不上。
RejectedExecutionHandler handler:這玩意兒就是拋出異常專用的,好比上面提到的兩個錯誤發生了,就會由這個handler拋出異常,根本用不上。
咱們怎麼知道new一個ThreadPoolExecutor,大概知道各個參數是幹嗎的,但是我new完了,怎麼向線程池提交一個要執行的任務啊?
ThreadPoolExecutor.execute(Runnable command)
經過ThreadPoolExecutor.execute(Runnable command)方法便可向線程池內添加一個任務。
這裏給總結一下,當一個任務被添加進線程池時,執行策略:
+++++++++++++++++++++++++++我是分割線++++++++++++++++++++++++++++
若是你不想本身寫一個線程池,Java經過Executors提供了四種線程池,這四種線程池都是直接或間接配置ThreadPoolExecutor的參數實現的。
1.可緩存線程池CachedThreadPool()
源碼:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
根據源碼能夠看出:
建立方法:
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 => 最大線程數即maximumPoolSize ExecutorService 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 結合了一下。
建立:
//nThreads => 最大線程數即maximumPoolSize ExecutorService 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 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。