漫談Java 線程池ThreadPoolExecutor

參考資料
https://www.cnblogs.com/dolphin0520/p/3932921.html
https://www.jianshu.com/p/896b8e18501b

簡介

Java線程池的核心類爲ThreadPoolExecutor
線程池中的FixedThreadPool、SingleThreadExecutor、CachedThreadPool、ScheduledThreadPool底層均是調用了ThreadPoolExecutor的構造方法初始化的線程池
線程池與連接池的原理類似,省去了頻繁的初始化和銷燬的步驟,節省了時間,維護着活躍的線程供調用。

##ThreadPoolExecutor構造方法和基本原理

1、ThreadPoolExecutor構造方法

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

通過源碼可以看到ThreadPoolExecutor核心的構造方法,下面來分析下具體的參數的含義
corePoolSize ---- 保持在池中的線程數,即使他們是空閒的
maximumPoolSize — 池中允許的最大線程數
keepAliveTime ---- 當線程數大於corePoolSize,多餘的空閒線程在終止前等待新任務的最大時間。
unit ---- keepAliveTime 中參數的時間單位
workQueue ---- 任務執行前用於保存任務的隊列
threadFactory ---- 執行程序創建新線程時要使用的工廠
handler ---- 由於達到線程邊界和隊列容量而阻塞執行時使用的處理程序

  • 1、workQueue

workQueue的類型爲BlockingQueue,通常可以取下面三種類型:
1)ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小;
2)LinkedBlockingQueue:基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默認爲Integer.MAX_VALUE;(默認的就是所謂的無界隊列,maximumPoolSize因此也就沒有了意義)
3)synchronousQueue:這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。

2、基本原理

這個樣例我是參考的,感覺說的很好
corePoolSize在很多地方被翻譯成核心池大小,其實我的理解這個就是線程池的大小。舉個簡單的例子:
假如有一個工廠,工廠裏面有10個工人,每個工人同時只能做一件任務。
因此只要當10個工人中有工人是空閒的,來了任務就分配給空閒的工人做;
當10個工人都有任務在做時,如果還來了任務,就把任務進行排隊等待;
如果說新任務數目增長的速度遠遠大於工人做任務的速度,那麼此時工廠主管可能會想補救措施,比如重新招4個臨時工人進來;
然後就將任務也分配給這4個臨時工人做;
如果說着14個工人做任務的速度還是不夠,此時工廠主管可能就要考慮不再接收新的任務或者拋棄前面的一些任務了。
當這14個工人當中有人空閒時,而新任務增長的速度又比較緩慢,工廠主管可能就考慮辭掉4個臨時工了,只保持原來的10個工人,畢竟請額外的工人是要花錢的。

這個例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。
也就是說corePoolSize就是線程池大小,maximumPoolSize在我看來是線程池的一種補救措施,即任務量突然過大時的一種補救措施。

示例:
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,10L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(5));
如下線程池最多能接受10個線程,等待隊列裏5個,最大線程數5個,多的線程請求將會被拒絕
image.png

簡單代碼示例

我們這邊有個需求,就是從mysql拉取數據寫入到HDFS上,因爲mysql支持分頁,所以我們這邊打算就用線程池來做

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 3, 1,
        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
        new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 3; i++) {
	String sql = "select * from DWA.DWA_DEMO limit " + (i * avg + 1) + "," + (avg);
	System.out.println(sql);
	threadPool.submit(new MysqlRunable(sql,bw));
}
threadPool.shutdown();
while (!threadPool.isTerminated()) {
}

其中threadPool.isTerminated() 是爲了等待所有線程執行完畢
我們這邊的workQueue選擇的是LinkedBlockingQueue無界隊列,因爲也考慮到隊列各自獨立而且必須執行。

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