#多線程學習——ThreadPoolExecutor之不允許使用Executors創建(1)

在阿里巴巴開發手冊的併發處理那章提到線程池不允許使用Executor來創建,要用ThreadPoolExecutor的方式來創建
如圖
本文就來分析一下爲什麼不能使用Executor來創建。其實手冊下面說明已經說了
1)FixedThreadPool 和 SingleThreadPool:
允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允許的創建線程數量爲 Integer**.MAX_VALUE**,可能會創建大量的線程,從而導致 OOM。

那爲什麼允許請求隊列和允許創建線程爲無窮大的時候會導致OOM呢?這就要先說一下,線程池的工作原理:
在這裏插入圖片描述
從上圖可知,當一個任務到線程池其工作原理:
1.如果線程池中的線程數量少於corePoolSize(核心線程數量),那麼會直接開啓一個新的核心線程來執行任務,即使此時有空閒線程存在.
2.如果線程池中線程數量大於等於corePoolSize(核心線程數量),那麼任務會被插入到任務隊列中排隊,等待被執行.此時並不添加新的線程.
3.如果在步驟2中由於任務隊列已滿導致無法將新任務進行排隊,這個時候有兩種情況:
線程數量 [未] 達到maximumPoolSize(線程池最大線程數) , 立刻啓動一個非核心線程來執行任務.
線程數量 [已] 達到maximumPoolSize(線程池最大線程數) , 拒絕執行此任務.ThreadPoolExecutor會通過RejectedExecutionHandler(飽和策略),拋出RejectExecutionException異常。

OK.現在大致知道了流程池的處理邏輯,也就不難解釋爲什麼,

1.允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量請求,從而導致 OOM。
2.允許的創建線程數量爲 Integer**.MAX_VALUE**,可能會創建大量線程,從而導致 OOM。

那有同學就會問,爲什麼
1.FixedThreadPool 和 SingleThreadPool允許請求隊列長度爲無窮大。
2.CachedThreadPool 和 ScheduledThreadPool允許創建線程數量爲無窮大。

這裏我們就可以看一下他的源碼就知道了。
1.FixedThreadPool 和 SingleThreadPool

//SingleThreadPool源碼
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}
//newFixedThreadPool源碼
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

不難發現,他們都是用了LinkedBlockingQueue<Runnable>隊列來接受來不及處理的任務。
問題就是在這個隊列裏,它的默認構造器是Integer.MAX_VALUE。

  /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

2.CachedThreadPool 和 ScheduledThreadPool

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

從上述源碼中我們不難發現,這兩種線程池,線程池大小是不固定的,雖然newScheduledThreadPool傳如一個線程數,但是這個數字只是核心線程數,可能還會擴容,直至Integer.MAX_VALUE

OK,知道Executor創建線程池有資源耗盡的風險,那我們應該怎麼解決了?

			等下一篇,再告訴你,拜拜。

及時學習新知識,不用加晚班

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