Java 線程池ExecutorService詳解及實例代碼
Java 線程池ExecutorService
1.線程池
1.1什么情況下使用線程池
- 單個任務(wù)處理的時間比較短.
- 將需處理的任務(wù)的數(shù)量大.
1.2使用線程池的好處
- 減少在創(chuàng)建和銷毀線程上所花的時間以及系統(tǒng)資源的開銷.
- 如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量線程而導(dǎo)致消耗系統(tǒng)內(nèi)存以及”過度切換”;
2.ExecutorService和Executors
2.1簡介
ExecutorService是一個接口,繼承了Executor,
public interface ExecutorService extend Executor{
}
Executor也是一個接口,該接口只包含一個方法:
public interface Executor {
void execute(Runnable command);
}
Java里面的線程池的頂級接口是Excutor,但是嚴格意義上來說>>Exector并不是一個線程池,而只是一個執(zhí)行線程的工具,真正的線程>池接口是ExecutorService.
3.Executors
它是一個靜態(tài)工廠類,它能生產(chǎn)不同類型的線程池,部分源碼如下:
public class Executors {
//newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
//newCacheThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
//newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//newStringooo
}
先看一個具體的例子,用例子來說明它們之間的異同.
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* Created by yang on 16-7-11.
*/
public class Ch09_Executor {
private static void run(ExecutorService threadPool) {
for (int i = 1; i < 5; i++) {
final int taskID=i;
threadPool.execute(new Runnable() {
@Override
public void run() {
for(int i=1;i<5;i++){
try{
Thread.sleep(20);
}catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("第"+taskID+"次任務(wù)的第"+i+"次執(zhí)行");
}
}
});
}
threadPool.shutdown();
}
public static void main(String[] args) {
//創(chuàng)建可以容納3個線程的線程池
ExecutorService fixedThreadPool= Executors.newFixedThreadPool(3);
//線程池的大小會根據(jù)執(zhí)行的任務(wù)動態(tài)的分配
ExecutorService cacheThreadPool=Executors.newCachedThreadPool();
//創(chuàng)建單個線程的線程池,如果當前線程在執(zhí)行任務(wù)時突然中斷,則會創(chuàng)建一個新的線程替換它繼續(xù)執(zhí)行.
ExecutorService singleThreadPool=Executors.newSingleThreadExecutor();
//效果類似于Timer定時器
ScheduledExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(3);
// run(fixedThreadPool); //(1)
//run(cacheThreadPool); //(2)
// run(singleThreadPool); //(3)
// run(scheduledThreadPool); //(4)
}
}
4. 4種常用的線程池
4.1 CachedThreadPool
CachedThreadPool會創(chuàng)建一個緩存區(qū),將初始化的線程緩存起來,會終止并且從緩存中移除已有6秒未被使用的線程.
如果線程有可用,就使用之前創(chuàng)建好的線程.如果線程沒有可用的,就新創(chuàng)建線程.
.重用:
緩存型池子,先看看池中有沒有以前建立的線程,如果有,就reuse,如果沒有,就新建一個新的線程加入池中,
使用場景:
緩存型池子通常用于執(zhí)行一些生存期很短的異步型任務(wù),因此在一些面向連接的Daemon型SERVER中用地不多.
超時:
能reuse的線程,必須是timeout IDLE內(nèi)的池中線程,缺省timeout是60s,超過這個IDLE時長,線程實例將被終止及移除池.
結(jié)束:
放入CachedThreadPool的線程不必擔心其結(jié)束,超過TIMEOUT不活動,其會被自動終止.
實例解說:
去掉(2)的注釋,運行,得到的運行結(jié)果如下:
第1次任務(wù)的第1次執(zhí)行 第3次任務(wù)的第1次執(zhí)行 第2次任務(wù)的第1次執(zhí)行 第4次任務(wù)的第1次執(zhí)行 第3次任務(wù)的第2次執(zhí)行 第1次任務(wù)的第2次執(zhí)行 第2次任務(wù)的第2次執(zhí)行 第4次任務(wù)的第2次執(zhí)行 第3次任務(wù)的第3次執(zhí)行 第1次任務(wù)的第3次執(zhí)行 第2次任務(wù)的第3次執(zhí)行 第4次任務(wù)的第3次執(zhí)行 第3次任務(wù)的第4次執(zhí)行 第2次任務(wù)的第4次執(zhí)行 第4次任務(wù)的第4次執(zhí)行 第1次任務(wù)的第4次執(zhí)行
從結(jié)果可以看出,4個任務(wù)是交替執(zhí)行的.
4.2FixedThreadPool
在FixedThreadPool中,有一個固定大小的池,
如果當前需要執(zhí)行的任務(wù)超過池大小,那么多出去的任務(wù)處于等待狀態(tài),直到有空閑下來的線程執(zhí)行任務(wù)。
如果當前需要執(zhí)行的任務(wù)小于池大小,空閑線程不會被銷毀.
重用:
fixedThreadPool與cacheThreadPool差不多,也是能reuse就用,但不能隨時建新的線程
固定數(shù)目
其獨特之處在于,任意時間點,最多只能有固定數(shù)目的活動線程存在,此時如果有新的線程要建立,只能放在另外的隊列中等待,直到當前的線程中某個線程終止直接被移出池子
超時:
和cacheThreadPool不同,F(xiàn)ixedThreadPool沒有IDLE機制
使用場景:
所以FixedThreadPool多數(shù)針對一些很穩(wěn)定很固定的正規(guī)并發(fā)線程,多用于服務(wù)器
源碼分析:
從方法的源代碼看,cache池和fixed 池調(diào)用的是同一個底層池,只不過參數(shù)不同.
fixed池線程數(shù)固定,并且是0秒IDLE(無IDLE)
cache池線程數(shù)支持0-Integer.MAX_VALUE(顯然完全沒考慮主機的資源承受能力),60秒IDLE
實例解說:
去掉(1)的注釋,運行結(jié)果如下:
第1次任務(wù)的第1次執(zhí)行 第3次任務(wù)的第1次執(zhí)行 第2次任務(wù)的第1次執(zhí)行 第1次任務(wù)的第2次執(zhí)行 第3次任務(wù)的第2次執(zhí)行 第2次任務(wù)的第2次執(zhí)行 第1次任務(wù)的第3次執(zhí)行 第3次任務(wù)的第3次執(zhí)行 第2次任務(wù)的第3次執(zhí)行 第1次任務(wù)的第4次執(zhí)行 第3次任務(wù)的第4次執(zhí)行 第2次任務(wù)的第4次執(zhí)行 第4次任務(wù)的第1次執(zhí)行 第4次任務(wù)的第2次執(zhí)行 第4次任務(wù)的第3次執(zhí)行 第4次任務(wù)的第4次執(zhí)行
創(chuàng)建了一個固定大小的線程池,容量是為3,然后循環(huán)執(zhí)行4個任務(wù),由輸出結(jié)果可以看出,前3個任務(wù)首先執(zhí)行完,然后空閑下來的線程去執(zhí)行第4個任務(wù).
4.3SingleThreadExecutor
- SingleThreadExector得到的是一個單個線程,這個線程會保證你的任務(wù)執(zhí)行完成.
- 單例線程,任意時間池中只能有一個線程
- 如果當前線程意外終止,會創(chuàng)建一個新的線程繼續(xù)執(zhí)行任務(wù),這和我們直接創(chuàng)建線程不同,也和newFixedThreadPool(1)不同.
- 用的是和cache池和fixed池相同的底層池,但線程數(shù)目是1-1,0秒IDLE(無IDLE)
去掉(3)注釋. 看執(zhí)行結(jié)果如下:
第1次任務(wù)的第1次執(zhí)行 第1次任務(wù)的第2次執(zhí)行 第1次任務(wù)的第3次執(zhí)行 第1次任務(wù)的第4次執(zhí)行 第2次任務(wù)的第1次執(zhí)行 第2次任務(wù)的第2次執(zhí)行 第2次任務(wù)的第3次執(zhí)行 第2次任務(wù)的第4次執(zhí)行 第3次任務(wù)的第1次執(zhí)行 第3次任務(wù)的第2次執(zhí)行 第3次任務(wù)的第3次執(zhí)行 第3次任務(wù)的第4次執(zhí)行 第4次任務(wù)的第1次執(zhí)行 第4次任務(wù)的第2次執(zhí)行 第4次任務(wù)的第3次執(zhí)行 第4次任務(wù)的第4次執(zhí)行
四個任務(wù)是順序執(zhí)行的.
4.4 ScheduledThreadPool
ScheduledThreadPool是一個固定大小的線程池,與FixedThreadPool類似,執(zhí)行的任務(wù)是定時任務(wù).
去掉(4)的注釋得到的結(jié)果和FixedThreadPool得到的結(jié)果相同,ScheduledThreadPool的主要沒有在這里,而是定時任務(wù),看下面這個例子:
package thread;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Created by yang on 16-7-11.
*/
public class MyScheduledTask implements Runnable {
private String tname;
public MyScheduledTask(String name){
this.tname=name;
}
public void run(){
System.out.println(tname+"任務(wù)時延時2秒執(zhí)行!");
}
public static void main(String[] args) {
ScheduledExecutorService scheduledPool= Executors.newScheduledThreadPool(2);
ScheduledExecutorService singSchedulePool=Executors.newSingleThreadScheduledExecutor();
MyScheduledTask mt1=new MyScheduledTask("mt1");
MyScheduledTask mt2=new MyScheduledTask("mt2");
//以scheduledThreadPool啟動mt1任務(wù)執(zhí)行
scheduledPool.schedule(mt1,2, TimeUnit.SECONDS);
//用singlescheduledthreadPool啟動mt2;
singSchedulePool.schedule(mt2,2000,TimeUnit.MILLISECONDS);
scheduledPool.shutdown();
singSchedulePool.shutdown();
}
}
結(jié)果:
mt1任務(wù)時延時2秒執(zhí)行! mt2任務(wù)時延時2秒執(zhí)行!
在程序運行2秒后,才會有結(jié)果顯示,說明線程在2秒后執(zhí)行的.
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
List集合中對數(shù)據(jù)實現(xiàn)多重規(guī)則進行排序的案例
今天小編就為大家分享一篇關(guān)于List集合中對數(shù)據(jù)實現(xiàn)多重規(guī)則進行排序的案例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
深入了解HttpClient的ResponseHandler接口
這篇文章主要為大家介紹了深入了解HttpClient的ResponseHandler接口,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
使用GSON庫轉(zhuǎn)換Java對象為JSON對象的進階實例詳解
這篇文章主要介紹了使用GSON庫轉(zhuǎn)換Java對象為JSON對象的進階實例詳解,包括注冊TypeAdapter及處理Enum類型等實際運用中可能遇到的一些復(fù)雜問題,需要的朋友可以參考下2016-06-06
一文教你如何通過三級緩存解決Spring循環(huán)依賴
這篇文章主要介紹了如何通過三級緩存解決?Spring?循環(huán)依賴,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考價值,需要的朋友可以參考下2023-07-07
IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版)
這篇文章主要介紹了IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版),推薦第四種方法,第四種方法摸索研究后得出,親測好用,需要的朋友參考下吧2023-10-10

