Java經常使用四大線程池用法以及ThreadPoolExecutor詳解

爲何用線程池?程序員

  • 1.建立/銷燬線程伴隨着系統開銷,過於頻繁的建立/銷燬線程,會很大程度上影響處-理效率
  • 2.線程併發數量過多,搶佔系統資源從而致使阻塞
  • 3.對線程進行一些簡單的管理

在Java中,線程池的概念是Executor這個接口,具體實現爲ThreadPoolExecutor類,學習Java中的線程池,就能夠直接學習他了對線程池的配置,就是對ThreadPoolExecutor構造函數的參數的配置緩存

1、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)

下面來解釋下各個參數:併發

  • int corePoolSize:該線程池中核心線程數最大值

核心線程:線程池新建線程的時候,若是當前線程總數小於corePoolSize,則新建的是核心線程,若是超過corePoolSize,則新建的是非核心線程核心線程默認狀況下會一直存活在線程池中,即便這個核心線程啥也不幹(閒置狀態)。
若是指定ThreadPoolExecutor的allowCoreThreadTimeOut這個屬性爲true,那麼核心線程若是不幹活(閒置狀態)的話,超過必定時間(時長下面參數決定),就會被銷燬掉。ide

  • 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 : 天spa

  • BlockingQueue workQueue:該線程池中的任務隊列:維護着等待執行的Runnable對象

當全部的核心線程都在幹活時,新添加的任務會被添加到這個隊列中等待處理,若是隊列滿了,則新建非核心線程執行任務。
經常使用的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拋出異常,根本用不上。

2、向ThreadPoolExecutor添加任務

咱們怎麼知道new一個ThreadPoolExecutor,大概知道各個參數是幹嗎的,但是我new完了,怎麼向線程池提交一個要執行的任務啊?

ThreadPoolExecutor.execute(Runnable command)

經過ThreadPoolExecutor.execute(Runnable command)方法便可向線程池內添加一個任務。

3、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>());
    }

根據源碼能夠看出:

  1. 這種線程池內部沒有核心線程,線程的數量是有沒限制的。
  2. 在建立任務時,如有空閒的線程時則複用空閒的線程,若沒有則新建線程。
  3. 沒有工做的線程(閒置狀態)在超過了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>());
}

根據源碼能夠看出:

  1. 該線程池的最大線程數等於核心線程數,因此在默認狀況下,該線程池的線程不會由於閒置狀態超時而被銷燬。
  2. 若是當前線程數小於核心線程數,而且也有閒置線程的時候提交了任務,這時也不會去複用以前的閒置線程,會建立新的線程去執行任務。若是當前執行任務數大於了核心線程數,大於的部分就會進入隊列等待。等着有閒置的線程來執行這個任務。

建立方法:

//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>()));
}

根據源碼能夠看出:

  1. 有且僅有一個工做線程執行任務
  2. 全部任務按照指定順序執行,即遵循隊列的入隊出隊規則

建立方法:
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 結合了一下。

  1. 不只設置了核心線程數,最大線程數也是Integer.MAX_VALUE。
  2. 這個線程池是上述4箇中爲惟一個有延遲執行和週期執行任務的線程池。

建立:

//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 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。

相關文章
相關標籤/搜索