Java:多線程,線程池,ThreadPoolExecutor詳解

1. ThreadPoolExecutor的一個經常使用的構造方法

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
    TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) 

參數說明:java

-corePoolSize       線程池中所保存的核心線程數。線程池啓動後默認是空的,只有任務來臨時纔會建立線程以處理請求。prestartAllCoreThreads方法能夠在線程池啓動後即啓動全部核心線程以等待任務。數組

-maximumPoolSize  線程池容許建立的最大線程數。當workQueue使用無界隊列時(如:LinkedBlockingQueue),則此參數無效。less

-keepAliveTime      當前線程池線程總數大於核心線程數時,終止多餘的空閒線程的時間。ide

-unit          keepAliveTime參數的時間單位。this

-workQueue       工做隊列,若是當前線程池達到核心線程數時(corePoolSize),且當前全部線程都處於活動狀態,則將新加入的任務放到此隊列中。下面僅列幾個經常使用的:spa

  • ArrayBlockingQueue:  基於數組結構的有界隊列,此隊列按FIFO原則對任務進行排序。若是隊列滿了還有任務進來,則調用拒絕策略。
  • LinkedBlockingQueue:  基於鏈表結構的無界隊列,此隊列按FIFO原則對任務進行排序。由於它是無界的,根本不會滿,因此採用此隊列後線程池將忽略拒絕策略(handler)參數;同時還將忽略最大線程數(maximumPoolSize)等參數
  • SynchronousQueue:   直接將任務提交給線程而不是將它加入到隊列,實際上此隊列是空的。每一個插入的操做必須等到另外一個調用移除的操做;若是新任務來了線程池沒有任何可用線程處理的話,則調用拒絕策略。其實要是把maximumPoolSize設置成無界(Integer.MAX_VALUE)的,加上SynchronousQueue隊列,就等同於Executors.newCachedThreadPool()。
  • PriorityBlockingQueue: 具備優先級的隊列的有界隊列,能夠自定義優先級;默認是按天然排序,可能不少場合並不合適。

-handler          拒絕策略,當線程池與workQueue隊列都滿了的狀況下,對新加任務採起的策略。線程

  • AbortPolicy:           拒絕任務,拋出RejectedExecutionException異常。默認值。
  • CallerRunsPolicy:   A handler for rejected tasks that runs the rejected task directly in the calling thread of the execute method, unless the executor has been shut down, in which case the task is discarded(沒太弄懂意思,看不太懂,程序模擬半天也沒得出啥結論。)
  • DiscardOldestPolicy:  若是執行程序還沒有關閉,則位於工做隊列頭部的任務將被刪除,而後重試執行程序(若是再次失敗,則重複此過程)。這樣的結果是最後加入的任務反而有可能被執行到,先前加入的都被拋棄了。
  • DiscardPolicy:      加不進的任務都被拋棄了,同時沒有異常拋出

2. 詳解及示範

  1. 一個任務進來(Runnable)時,若是核心線程數(corePoolSize未達到,則直接建立線程處理該任務;若是核心線程數已經達到則該任務進入工做隊列(workQueue)。若是工做隊列滿了(只能是有界隊列),則檢查最大線程數(maximumPoolSize是否達到,若是沒達到則建立線程處理任務(FIFO);若是最大線程數據也達到了,則調用拒絕策略(handler)。
  2. 若是workQueue使用LinkedBlockingQueue隊列,由於它是無界的,隊列永遠不會滿,因此maximumPoolSize參數是沒有意義的,一樣keepAliveTimeunithandler三個參數都無心義。
  3. 若是workQueue使用ArrayBlockingQueue隊列,那麼當心,由於此隊列是有界的,必須當心處理拒絕策略。你看人家Executors類,壓根就不使用ArrayBlockingQueue隊列。
  4. 正常狀況下,若是使用Executors靜態工廠生成的幾種經常使用線程池可以知足要求,建議就用它們吧。本身控制全部細節挺不容易的。 
package com.clzhang.sample.thread;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest3 {
    static class MyThread implements Runnable {
        private String name;

        public MyThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            // 作點事情
            try {
                Thread.sleep(1000);
                    
                System.out.println(name + " finished job!") ;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 建立線程池,爲了更好的明白運行流程,增長了一些額外的代碼
//        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(2);
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
//        BlockingQueue<Runnable> queue = new PriorityBlockingQueue<Runnable>();
//        BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();

// AbortPolicy/CallerRunsPolicy/DiscardOldestPolicy/DiscardPolicy ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy()); // 向線程池裏面扔任務 for (int i = 0; i < 10; i++) { System.out.println("當前線程池大小[" + threadPool.getPoolSize() + "],當前隊列大小[" + queue.size() + "]"); threadPool.execute(new MyThread("Thread" + i)); } // 關閉線程池 threadPool.shutdown(); } }

輸出(採用LinkedBlockingQueue隊列):rest

當前線程池大小[0],當前隊列大小[0]
當前線程池大小[1],當前隊列大小[0]
當前線程池大小[2],當前隊列大小[0]
當前線程池大小[2],當前隊列大小[1]
當前線程池大小[2],當前隊列大小[2]
當前線程池大小[2],當前隊列大小[3]
當前線程池大小[2],當前隊列大小[4]
當前線程池大小[2],當前隊列大小[5]
當前線程池大小[2],當前隊列大小[6]
當前線程池大小[2],當前隊列大小[7]
Thread1 finished job!
Thread0 finished job!
Thread3 finished job!
Thread2 finished job!
Thread4 finished job!
Thread5 finished job!
Thread6 finished job!
Thread7 finished job!
Thread8 finished job!
Thread9 finished job!
code

3. 回頭看看Executors靜態工廠方法生成線程池的源代碼

Executors.newSingleThreadExecutor()blog

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

Executors.newFixedThreadPool()

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

Executors.newCachedThreadPool()

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

經過上述代碼能夠發現,用Executors靜態工廠生成的幾種經常使用線程池,均可以向裏面插入n多任務:要麼workQueue是無界的,要麼maximumPoolSize是無界的。

相關文章
相關標籤/搜索