深入淺出線程池那些事

近期在複習線程池的面試題以及前兩天面試一家公司實習生時被問到關於線程池。由於是第一次,所有舔着臉來和大家分享。如有不妥,還望指正。

1.什麼是線程池?百度的解釋如下:

線程池簡單來說就是一個池子裏面有一定量的線程,需要時從池子裏拿,而不需要每次new 一個實例,增大開銷。

2.使用線程池有什麼好處
這裏我分享幾個鏈接,感覺寫的很好,我要自己寫無非也是照搬照抄(手動滑稽)。
https://www.jianshu.com/p/210eab345423
https://blog.csdn.net/u011394071/article/details/52893495
https://blog.csdn.net/leikun153/article/details/81609392

3.線程池重要的四個類
下面重點來了,學習Java,知其然和知其所以然也是有很大區別的,而使用線程池時非常簡單的,無非new 一個ThreadPoolExecutor的實例。Java封裝了ThreadPoolExecutor這個類,我們學習也應該深入源碼去學習,真正做到知其然,知其所以然。
-ThreadPoolExecutor:
源碼幾百行,我截取其中幾個重要的方法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

   public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

以上是ThreadPoolExecutor的四個核心方法,裏面的參數是維持一個線程池所必須的。這些方法我不一一解釋,這個不是我要講解的重點,也很容易理解。
-AbstractExecutorService
它是ThreadPoolExecutor的父類,是一個抽象類,實現了ExecutorService接口,它有以下核心方法。

 1.     public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
 2.     public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }
 3.     public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

很顯然這是三個類似於重載的方法,submit()方法是向線程池提交線程任務的一個方法,與execute()方法類似,後面我們會說到execute()方法。
1. 第一個方法中傳入了一個Runnable 類型的參數,返回值爲一個Future對象,怎麼理解這個Future對象呢,我們不談線程池提交任務,就我們日常生活中,你把快遞寄出去,然後你可以通過快遞100等APP查詢到他的物流去向,你可以知道結果如何。Future對象就扮演了這樣一個角色,他不參與任務,通過這個對象的get()方法可以知道任務是否成功。
2. 第二個方法,傳入了Runnable 類型的參數和一個泛型參數result,這個又是什麼呢,這個result可以將線程中的一些結果(比如異常)保存起來,方便查看整個過程中發生了什麼。
3. 第三個方法,傳入了一個 Callable對象,這也說明sumbit()方法既可以傳入Callable也可以傳Runnable ,這是和execute()方法的一個區別。
-ExecutorService
這個接口定義了一些除了AbstractExecutorService中實現的方法,還有一些其他方法

submit是我們熟悉的。shutdown方法是可以關閉線程池的任務。

-Executor
它是最底層的一個接口, 它裏面只定義了一個方法:
void execute(Runnable command);
}
這個方法向線程池提交任務。

3.sumbit()和execute()方法方法的區別
(1).execute提交的方式只能提交一個Runnable的對象,且該方法的返回值是void,也即是提交後如果線程運行後,和主線程就脫離了關係了,當然可以設置一些變量來獲取到線程的運行結果。並且當線程的執行過程中拋出了異常通常來說主線程也無法獲取到異常的信息的,只有通過ThreadFactory主動設置線程的異常處理類才能感知到提交的線程中的異常信息。
(2).submit提交的方式情況,上面我們已經詳細說過,但從源碼分析,我們可以看出他實際上是調用了execute()方法

這裏寫圖片描述

4.ThreadPoolExecutor的重要參數
博主在面試過程中被問到,需要什麼參數來維持一個線程池,一開始聽到維持兩個字我懵逼了。其實無非是說介紹一下線程池的核心參數。
這裏我簡要提一下。下一篇我將詳細介紹ThreadPoolExecutor線程池參數設置技巧 。

  • corePoolSize:核心線程數 核心線程會一直存活,及時沒有任務需要執行
    當線程數小於核心線程數時,即使有線程空閒,線程池也會優先創建新線程處理

  • 設置allowCoreThreadTimeout=true(默認false)時,核心線程會超時關閉

  • queueCapacity:任務隊列容量(阻塞隊列) 當核心線程數達到最大時,新任務會放在隊列中排隊等待執行

  • maxPoolSize:最大線程數 當線程數>=corePoolSize,且任務隊列已滿時。線程池會創建新線程來處理任務
    當線程數=maxPoolSize,且任務隊列已滿時,線程池會拒絕處理任務而拋出異常 keepAliveTime:線程空閒時間
    當線程空閒時間達到keepAliveTime時,線程會退出,直到線程數量=corePoolSize
    如果allowCoreThreadTimeout=true,則會直到線程數量=0

  • allowCoreThreadTimeout:允許核心線程超時
  • rejectedExecutionHandler:任務拒絕處理器

    如有錯誤,歡迎指正,小弟第一次寫博客。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章