【多線程 】ThreadPoolExecutor 介紹

阿里巴巴Android開發(fā)手冊對線程池使用的建議:

【推薦】ThreadPoolExecutor 設(shè)置線程存活時間(setKeepAliveTime),確??臻e時線程能被釋放。
【強制】線程池不允許使用Executors 去創(chuàng)建,而是通過ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風險。
說明:
Executors 返回的線程池對象的弊端如下:
FixedThreadPool 和SingleThreadPool :允許的請求隊列長度為
Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM;
CachedThreadPool 和ScheduledThreadPool :允許的創(chuàng)建線程數(shù)量為
Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而導致OOM。

正例:
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,
taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());

反例:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

大部分情況我們使用Executors工具類去構(gòu)建線程池,大概分為以下5種不同的線程池,如下
//構(gòu)建線程數(shù)為1的線程池,等價于 newFixedThreadPool(1) 所構(gòu)造出的線程池
Executors.newSingleThreadExecutor();
//構(gòu)建包含固定線程數(shù)的線程池,默認情況下,空閑線程不會被回收
Executors.newFixedThreadPool(1);
//構(gòu)建線程數(shù)不定的線程池,線程數(shù)量隨任務(wù)量變動,空閑線程存活時間超過60秒后會被回收
Executors.newCachedThreadPool();
//構(gòu)建核心線程數(shù)為 corePoolSize,可執(zhí)行定時任務(wù)的線程池
Executors.newScheduledThreadPool(1);
//等價于 newScheduledThreadPool(1)
Executors.newSingleThreadScheduledExecutor();
ThreadManager 工具類,創(chuàng)建ThreadToolExecutor需要7個參數(shù),如下
線程池-核心參數(shù).png
/**
 * <p>
 * 1.降低資源消耗
 * 可以重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
 * 2.提高響應(yīng)速度
 * 當任務(wù)到達時,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
 * 3.提高線程的可管理性
 * 線程是稀缺資源,如果無限制地創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進行統(tǒng)一分配、調(diào)優(yōu)和監(jiān)控。
 * <p>
 * 最頂層的接口Executor僅聲明了一個方法execute。
 * ExecutorService 接口在其父接口基礎(chǔ)上,聲明了包含但不限于shutdown、shutdownNow、submit、invokeAll等方法。
 * ScheduledExecutorService接口,
 * 則是聲明了一些和定時任務(wù)相關(guān)的方法:schedule、scheduleAtFixedRate等。
 * 線程池的核心實現(xiàn)是在ThreadPoolExecutor類中,
 * 我們使用Executors調(diào)用newFixedThreadPool、newSingleThreadExecutor和newCachedThreadPool
 * 等方法創(chuàng)建線程池均是ThreadPoolExecutor類型。
 */
public class ThreadManager {
    private static final String TAG = "ThreadManager";

    /**
     * 根據(jù)cup核心數(shù)設(shè)置線程池數(shù)量
     * <p>
     * 《Android開發(fā)藝術(shù)探索》一書中建議:
     * 核心線程數(shù)等于CPU核心數(shù)+1;
     */
    private static final int corePoolSize = Runtime.getRuntime().availableProcessors();

    /**
     * 最大線程池數(shù)量= cpu核心數(shù)*2+1
     * <p>
     * 《Android開發(fā)藝術(shù)探索》一書中建議:
     * 線程池的最大線程數(shù)等于CPU的核心數(shù)的2倍+1;
     */
    private static final int maximumPoolSize = corePoolSize * 2 + 1;

    /**
     * 等待線程的存活時間
     * 核心線程無超時機制,分核心線程的閑置時間為4秒;
     */
    private static final long keepAliveTime = 30;

    /**
     * 等待線程存活時間的單位
     * <p>
     * TimeUnit.MINUTES 代表六十秒的時間單位
     */
    private static final TimeUnit unit = TimeUnit.MINUTES;

    /**
     * 3、 線程資源回收策略
     * 考慮到系統(tǒng)資源是有限的,對于線程池超出 corePoolSize 數(shù)量的空閑線程應(yīng)進行回收操作。進行此操作存在一個問題,即回收時機。
     * 目前的實現(xiàn)方式是當線程空閑時間超過 keepAliveTime 后,進行回收。除了核心線程數(shù)之外的線程可以進行回收,核心線程內(nèi)的空閑線程也可以進行回收。
     * 回收的前提是allowCoreThreadTimeOut屬性被設(shè)置為 true,通過public void allowCoreThreadTimeOut(boolean) 方法可以設(shè)置屬性值。
     * <p>
     * 4、 排隊策略
     * 如上面線程創(chuàng)建規(guī)則所說的,當線程數(shù)量大于等于corePoolSize,workQueue未滿時,則緩存新任務(wù)。
     * 這里要考慮使用什么類型的容器緩存新任務(wù),通過 JDK 文檔介紹,
     * 我們可知道有3種類型的容器可供使用,分別是同步隊列,有界隊列和無界隊列。對于有優(yōu)先級的任務(wù),這里還可以增加優(yōu)先級隊列。
     * 以上所介紹的4種類型的隊列,對應(yīng)的實現(xiàn)類如下:
     * <p>
     * SynchronousQueue         同步隊列        該隊列不存儲元素,每個插入操作必須等待另一個線程調(diào)用移除操作,否則插入操作會一直阻塞
     * ArrayBlockingQueue       有界隊列        基于數(shù)組的阻塞隊列,按照 FIFO 原則對元素進行排序
     * LinkedBlockingQueue      無界隊列        基于鏈表的阻塞隊列,按照 FIFO 原則對元素進行排序
     * PriorityBlockingQueue    優(yōu)先級隊列   具有優(yōu)先級的阻塞隊列
     */
    private static final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();

    /**
     * 5、線程工廠??赏ㄟ^工廠為新建的線程設(shè)置更有意義的名字
     */
    private static final ThreadFactory threadFactory = Executors.defaultThreadFactory();

    /**
     * 6、拒絕策略
     * 當線程池和任務(wù)隊列均處于飽和狀態(tài)時,使用拒絕策略處理新任務(wù)。默認是 AbortPolicy,即直接拋出異常
     * <p>
     * 如上線程創(chuàng)建規(guī)則策略中所說,當線程數(shù)量大于等于 maximumPoolSize,且 workQueue 已滿,
     * 或者是當前線程池被關(guān)閉了則使用拒絕策略處理新任務(wù)。Java 線程池提供了4種拒絕策略實現(xiàn)類,
     * 如下:
     * <p></p>
     * AbortPolicy  丟棄新任務(wù),并拋出 RejectedExecutionException
     * DiscardPolicy    不做任何操作,直接丟棄新任務(wù)
     * DiscardOldestPolicy  丟棄隊列列首的元素,并執(zhí)行新任務(wù)
     * CallerRunsPolicy 會在線程池當前正在運行的Thread線程池中處理被拒絕的任務(wù)
     */
    private static final RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();

    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

    private ThreadManager() {
    }

    /**
     * 餓漢單例緩存線程池
     * 優(yōu)點:對象優(yōu)先創(chuàng)建,無須等待,效率高。
     * 缺點:申明靜態(tài)對象的時候就已經(jīng)初始化,一定程度上造成了資源的浪費。
     */
    private static final ThreadManager INSTANCE = new ThreadManager();

    public static ThreadManager get() {
        return INSTANCE;
    }

    /**
     * submit在執(zhí)行過程中與execute不一樣,不會拋出異常而是把異常保存在成員變量中,
     * 在FutureTask.get阻塞獲取的時候再把異常拋出來
     */
    public void execute(Runnable runnable) {
        executor.execute(runnable);
    }

    /**
     * 通過Future可以很輕易地獲得任務(wù)的執(zhí)行情況,比如是否執(zhí)行完成、是否被取消、是否異常等等
     */
    public Future<?> submit(Runnable runnable) {
        return executor.submit(runnable);
    }

    /**
     * 調(diào)用 shutdown 和 shutdownNow 方法關(guān)閉線程池后,就不能再向線程池提交新任務(wù)了。
     * 對于處于關(guān)閉狀態(tài)的線程池,會使用拒絕策略處理新提交的任務(wù)。
     * <p>
     * shutdown 會將線程池的狀態(tài)設(shè)置為SHUTDOWN,同時該方法還會中斷空閑線程
     */
    public void shutdown() {
        executor.shutdown();
    }

    /**
     * shutdownNow 則會將線程池狀態(tài)設(shè)置為STOP,并嘗試中斷所有的線程
     */
    public void shutdownNow() {
        executor.shutdownNow();
    }
}
/**
 * 餓漢單例緩存線程池
 * 優(yōu)點:對象優(yōu)先創(chuàng)建,無須等待,效率高。
 * 缺點:申明靜態(tài)對象的時候就已經(jīng)初始化,一定程度上造成了資源的浪費。
 */
public class ThreadUtils {

    private static final ThreadUtils INSTANCE = new ThreadUtils();
    private final ExecutorService threadPool = Executors.newCachedThreadPool();

    private ThreadUtils() {
        //構(gòu)建線程數(shù)為1的線程池,等價于 newFixedThreadPool(1) 所構(gòu)造出的線程池
        Executors.newSingleThreadExecutor();
        //構(gòu)建包含固定線程數(shù)的線程池,默認情況下,空閑線程不會被回收
        Executors.newFixedThreadPool(1);
        //構(gòu)建線程數(shù)不定的線程池,線程數(shù)量隨任務(wù)量變動,空閑線程存活時間超過60秒后會被回收
        Executors.newCachedThreadPool();
        //構(gòu)建核心線程數(shù)為 corePoolSize,可執(zhí)行定時任務(wù)的線程池
        Executors.newScheduledThreadPool(1);
        //等價于 newScheduledThreadPool(1)
        Executors.newSingleThreadScheduledExecutor();
    }

    public static ThreadUtils get() {
        return INSTANCE;
    }

    public Future<?> submit(Runnable runnable) {
        if (runnable != null) {
            return threadPool.submit(runnable);
        }
        return null;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容