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

來源:互聯網
上載者:User

標籤: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>());    }

根據源碼可以看出:

  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 => 最大線程數即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>()));}

根據源碼可以看出:

  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 => 最大線程數即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詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.