線程池

什麼是線程池?
線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然後在創建線程後自動啓動這些任務。
線程池的好處:

  1. 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的消耗
  2. 提高響應速度。當任務到達時,任務可以不需要等到線程創建就能執行
  3. 提高現場的可管理性。線程是稀缺資源。如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控。但是,要做到合理利用線程池,必須對其實現原理瞭如指掌

線程池的執行流程:

1.線程池判斷核心線程池裏的線程是否都在執行任務。如果不是,則創建一個新的工作線程來執行任務。如果核心線程池裏的線程都在執行任務,則進入下個流程。
2.線程池判斷工作隊列是否已滿。如果工作隊列沒有滿,則將新提交的任務存儲在這個工作隊列裏,如果工作隊列滿了,則進入下個流程。
3.線程池判斷線程池的線程是否都處於工作狀態。如果沒有,則創建一個新的工作線程來執行任務。如果已經滿了,則交給飽和策略來處理這個任務。
如圖:

線程池執行流程

線程池的工作原理代碼實現:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException(); 
    int c = ctl.get();
    //1.當前池中線程比核心數少,新建一個線程執行任務
    if (workerCountOf(c) < corePoolSize) {   
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    //2.核心池已滿,但任務隊列未滿,添加到隊列中
    if (isRunning(c) && workQueue.offer(command)) {   
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))    //如果這時被關閉了,拒絕任務
            reject(command);
        else if (workerCountOf(recheck) == 0)    //如果之前的線程已被銷燬完,新建一個線程
            addWorker(null, false);
    }

    //3.核心池已滿,隊列已滿,試着創建一個新線程
    else if (!addWorker(command, false))
        reject(command);    //如果創建新線程失敗了,說明線程池被關閉或者線程池完全滿了,拒絕任務
}

而飽和策略有以下四種:

  • AbortPo**licy:直接拋出異常
  • CallerRunsP**olicy:只用調用者所在線程來運行任務
  • DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務
  • DiscardPolicy:不處理,丟棄掉

當然也可以根據應用場景需要來實現RejectedExecutionHandler接口自定義策略。如記錄日誌或者持久化存儲不能出爐的任務。

  • keepAliveTime(線程活動保持時間):線程池的工作線程空閒後,保持存活的時間。所以,如果任務很多,並且每個任務執行的時間比較短,可以調大時間,提高線程的利用率。
  • TimeUnit(線程活動保持時間的 單位):可選的單位有天(DAYS)、小時(HOURS)、分鐘(MINUTES)、毫秒(MILLISECONDS)、微妙(MICROSECONDS,千分之一毫秒)和納秒(NANOSECONDS,千分之一微妙)。

線程池分爲四種:

  • newCachedThreadPool:創建一個可緩存線程池
  • newFixedThreadPool:創建一個定長線程池
  • newScheduledThreadPool:創建一個核心線程數固定,而非核心線程數不固定的線程池
  • newSingleThreadExecutor:創建一個單線程化的線程池

詳細介紹:

  • newCachedThreadPool,是一種線程數量不定的線程池,並且其最大線程數爲Integer.MAX_VALUE,這個數是很大的,一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。但是線程池中的空閒線程都有超時限制,這個超時時長是60秒,超過60秒閒置線程就會被回收。調用execute將重用以前構造的線程(如果線程可用)。這類線程池比較適合執行大量的耗時較少的任務,當整個線程池都處於閒置狀態時,線程池中的線程都會超時被停止。
  • newFixedThreadPool 創建一個指定工作線程數量的線程池,每當提交一個任務就創建一個工作線程,當線程處於空閒狀態時,它們並不會被回收,除非線程池被關閉了,如果工作線程數量達到線程池初始的最大數,則將提交的任務存入到池隊列(沒有大小限制)中。由於newFixedThreadPool只有核心線程並且這些核心線程不會被回收,這樣它更加快速底相應外界的請求。
  • newScheduledThreadPool創建一個線程池,它的核心線程數量是固定的,而非核心線程數是沒有限制的,並且當非核心線程閒置時會被立即回收,它可安排給定延遲後運行命令或者定期地執行。這類線程池主要用於執行定時任務和具有固定週期的重複任務。
  • newSingleThreadExecutor這類線程池內部只有一個核心線程,以無界隊列方式來執行該線程,這使得這些任務之間不需要處理線程同步的問題,它確保所有的任務都在同一個線程中按順序中執行,並且可以在任意給定的時間不會有多個線程是活動的。

代碼演示:

public class ThreadPoolExecutorDemo {
    //newCachedThreadPool
    public static void CachedThreadPoolDemo() {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();     }
            cachedThreadPool.execute(new Runnable() {
                public void run() {
                    System.out.println(index);
                }
            });
        }
    }
//newFixedThreadPool
    public static void FixedThreadPoolDemo() {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
                public void run() {
                    try {
                        System.out.println(index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
//newScheduledThreadPool
    public static void SchedulThreadPoolDemo() {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        scheduledThreadPool.schedule(new Runnable() {
            public void run() {
                System.out.println("delay 3 seconds");
            }
            }, 3, TimeUnit.SECONDS);

    }

//newCachedThreadPool
    public static void SingleThreadExecutorDemo() {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        singleThreadExecutor.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    System.out.println(Thread.currentThread().getName()+i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        });
    }

    public static void main(String[] args) {
        CachedThreadPoolDemo();
        FixedThreadPoolDemo();
        SchedulThreadPoolDemo();
        SingleThreadExecutorDemo();
    }
}

在使用線程池時,Java給我們提供了創建線程池的一個類Executor,而我們創建時,一般使用它的子類ThreadPoolExecutor
繼承關係:

public class ThreadPoolExecutor extends AbstractExecutorService {}

ThreadPoolExecutor的幾種構造參數:

  • corePoolSize:核心線程的數量。默認是沒有超時的,也就是說就算線程閒置,也不會被處理。但是如果設置了allowCoreTimeOut爲true,那麼當核心線程閒置時,會被回收。
  • maximumPoolSize:最大線程池尺寸,被CAPACITY限制(2^29-1)。
  • keepAliveTime:閒置線程被回收的時間限制
  • unit:keepAliveTime的單位
  • workQueue:用於存放任務的隊列 四種 有界隊列 無界隊列 同步隊列 優先級隊列
  • threadFactory:創建線程的工廠類
  • handler:當任務執行失敗時,使用handler通知調用者
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章