ThreadPoolExecutor 的常用用法

ThreadPoolExecutor 的常用用法

平常工作中遇到併發場景的解決方案,一般會將大任務拆分成相互獨立的小任務,然後將這些小的任務提交給線程池去執行。

線程池的具體實現大致有ExecutorsnewFixedThreadPool()newCachedThreadPool()等等方法。但通過看它們的底層實現都是去new ThreadPoolExecutor(),且在工作中採用這種偏底層的實現方式也更多些,對自己寫的併發程序能有更好的控制。

下面以簡單程序的形式展現new ThreadPoolExecutor()的用法:

package org.zeng.test.concurrent;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by liu shuangzeng on 2017/11/11 21:14.
 */

public class ThreadPoolTest {

    /**
     * corePoolSize: 核心線程數;如果不設置AllowCoreThreadTimeOut=true,則不會被銷燬,即使處於閒置狀態
     * maximumPoolSize: 最大線程數;= 核心線程數 + 非核心線程數
     * keepAliveTime, unit: 非核心線程的閒置後的生命週期
     * BlockingQueue: 阻塞隊列;線程池提交任務時先會創建核心線程,如果核心線程不夠了,則會將任務塞到隊列中;如果隊列也滿了,則開始創建非核心線程處理任務;
     * 如果非核心線程也不夠用了,則新來的任務會進入 RejectedExecutionHandler 處理。。
     * 關於 BlockingQueue,雖然它是 Queue 的子接口,但是它的主要作用並不是容器,而是作爲線程同步的工具,它有一個特徵,
     * 當生產者試圖向 BlockingQueue 放入(put)元素,如果隊列已滿,則該線程被阻塞;當消費者試圖從 BlockingQueue 取出(take)元素,如果隊列已空,則該線程被阻塞。
     * @param size
     * @return
     */
    public static ThreadPoolExecutor getThreadPool(int size) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(size, 10, 30, TimeUnit.DAYS,
                new LinkedBlockingDeque<Runnable>(5), new ThreadFactory() {
            private final AtomicInteger atomicInteger = new AtomicInteger();

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "test-" + atomicInteger.incrementAndGet());
            }
        }, new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("隊列滿啦,而且線程數量達到最大數量啦!");
                try {
                    executor.getQueue().put(r);
                    System.out.println("卡住了嘛?");
                } catch (Exception e) {
                    System.out.println("塞入隊列異常啦!");
                }
                System.out.println("塞進隊列成功!");
            }
        });
        return threadPoolExecutor;
    }

    public static void main(String args[])  throws Exception{
        ThreadPoolExecutor threadPoolExecutor = getThreadPool(3);
        for (int i = 0; i < 20; i++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    work();
                }
            });
//            Future future = threadPoolExecutor.submit(new Runnable() {
//                @Override
//                public void run() {
//                    work();
//                }
//            });
//            future.get(); //會阻塞當前線程
            System.out.println("當前隊列的剩餘容量:" + threadPoolExecutor.getQueue().remainingCapacity());
        }
    }

    public static void work() {
        try{
            System.out.println("線程: " + Thread.currentThread().getName() + "進來啦!");
            Thread.sleep(1000l);
        } catch (Exception e) {

        }
    }
}

new ThreadPoolExecutor()的參數比較多,但理解了:線程池提交任務時先會創建核心線程,如果核心線程不夠了,則會將任務塞到隊列中;如果隊列也滿了,則開始創建非核心線程處理任務; 如果非核心線程也不夠用了,則新來的任務會進入RejectedExecutionHandler處理 這個過程也就不那麼難記憶和使用了。

注意的點:
1. 如果不爲BlockingQueue指定容量的化,它默認容量是Integer.MAX_VALUE,此時若maximumPoolSize > corePoolSize,那麼基本上永遠也不會去創建非核心線程,除非你的任務真的塞滿了Integer.MAX_VALUE大小的隊列.
2. 一種可以通俗理解的優先級: 核心線程 > 隊列 > 非核心線程 > RejectedExecutionHandler

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