JAVA四種執行緒池詳解
執行緒池的作用:
1. 執行緒池作用就是限制系統中執行執行緒的數量。
2. 根據系統的環境情況,可以自動或手動設定執行緒數量,達到執行的最佳效果;少了浪費了系統資源,多了造成系統擁擠效率不高。用執行緒池控制執行緒數量,其他執行緒排隊等候。一個任務執行完畢,再從佇列的中取最前面的任務開始執行。若佇列中沒有等待程序,執行緒池的這一資源處於等待。當一個新任務需要執行時,如果執行緒池中有等待的工作執行緒,就可以開始運行了;否則進入等待佇列。
為什麼要用執行緒池:
1.減少了建立和銷燬執行緒的次數,每個工作執行緒都可以被重複利用,可執行多個任務。
2.可以根據系統的承受能力,調整執行緒池中工作線執行緒的數目,防止因為消耗過多的記憶體,而把伺服器累趴下(每個執行緒需要大約1MB記憶體,執行緒開的越多,消耗的記憶體也就越大,最後宕機)。
Java裡面執行緒池的頂級介面是Executor,但是嚴格意義上講Executor並不是一個執行緒池,而只是一個執行執行緒的工具。真正的執行緒池介面是ExecutorService。
Java類庫中提供的執行緒池
Java的四種執行緒池
在這裡,我們詳細講解前四種執行緒池。Java通過Executors提供四種執行緒池,分別為:
- newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
- newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
- newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。
- newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
ThreadPoolExecutor
首先,我們熟悉一下最底層的ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
構造方法引數講解引數名 | 作用 |
corePoolSize | 核心執行緒池大小 |
maximumPoolSize | 最大執行緒池大小 |
keepAliveTime | 執行緒池中超過corePoolSize數目的空閒執行緒最大存活時間;可以allowCoreThreadTimeOut(true)使得核心執行緒到這個有效時間也將關閉 |
TimeUnit | keepAliveTime時間單位 |
workQueue | 阻塞任務佇列 |
threadFactory | 新建執行緒工廠 |
RejectedExecutionHandler | 當提交任務數超過maxmumPoolSize+workQueue之和時,任務會交給RejectedExecutionHandler來處理 |
需要注意的點:
1.當執行緒池小於corePoolSize時,新提交任務將建立一個新執行緒執行任務,即使此時執行緒池中存在空閒執行緒。
2.當執行緒池達到corePoolSize時,新提交任務將被放入workQueue中,等待執行緒池中任務排程執行
3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會建立新執行緒執行任務
4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理
5.當執行緒池中超過corePoolSize數目的執行緒,空閒時間達到keepAliveTime時,關閉空閒執行緒
6.當設定allowCoreThreadTimeOut(true)時,執行緒池中corePoolSize執行緒空閒時間達到keepAliveTime也將關閉
newCachedThreadPool(可快取執行緒池)
定義:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
構造一個緩衝功能的執行緒池,配置corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,以及一個無容量的阻塞佇列 SynchronousQueue,因此任務提交之後,將會建立新的執行緒執行;執行緒空閒超過60s將會銷燬
建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。示例程式碼如下:
ExecutorService es1 = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
es1.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": "+ index);
}
});
}
輸出:
pool-1-thread-1: 0
pool-1-thread-1: 1
pool-1-thread-1: 2
pool-1-thread-1: 3
pool-1-thread-1: 4
pool-1-thread-1: 5
pool-1-thread-1: 6
pool-1-thread-1: 7
pool-1-thread-1: 8
pool-1-thread-1: 9
可以看出,當下一個執行緒開始時,前一個執行緒使用完後延時了index秒,則前一個執行緒處於空閒狀態,後面的執行緒依次拿這個執行緒(pool-1-thread-1)使用。
newFixedThreadPool (定長執行緒池,佇列)
定義:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
構造一個固定執行緒數目的執行緒池,配置的corePoolSize與maximumPoolSize大小相同,同時使用了一個無界
LinkedBlockingQueue存放阻塞任務,因此多餘的任務將存在再阻塞佇列,不會由RejectedExecutionHandler處理。
示例程式碼如下:
ExecutorService es2 = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
es2.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": "+ index);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
輸出:pool-1-thread-1: 0
pool-1-thread-2: 1
pool-1-thread-3: 2
pool-1-thread-2: 3
pool-1-thread-1: 4
pool-1-thread-3: 5
pool-1-thread-2: 6
pool-1-thread-1: 7
pool-1-thread-3: 8
pool-1-thread-2: 9
每隔兩秒同時列印三個執行緒。定長執行緒池的大小最好根據系統資源進行設定。如Runtime.getRuntime().availableProcessors()。可參考PreloadDataCache。
newScheduledThreadPool
定義:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
構造有定時功能的執行緒池,配置corePoolSize,無界延遲阻塞佇列DelayedWorkQueue;有意思的是:maximumPoolSize=Integer.MAX_VALUE,由於DelayedWorkQueue是無界佇列,所以這個值是沒有意義的。
支援定時及週期性任務執行。這裡舉兩個例子(延遲+定時):
ScheduledExecutorService es3 = Executors.newScheduledThreadPool(5);
//延時3s再執行執行緒
es3.schedule(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
//先延遲3秒,然後每隔3s執行執行緒一次
es3.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds, and excute every 3 seconds");
}
}, 3, 3, TimeUnit.SECONDS);
輸出:
delay 3 seconds
delay 3 seconds, and excute every 3 seconds
delay 3 seconds, and excute every 3 seconds
delay 3 seconds, and excute every 3 seconds
delay 3 seconds, and excute every 3 seconds
.....................
延遲和定時任務互不影響,ScheduledExecutorService比Timer更安全,功能更強大
newSingleThreadExecutor
構造一個只支援一個執行緒的執行緒池,它只會用唯一的工作執行緒來執行任,配置corePoolSize=maximumPoolSize=1,無界阻塞佇列LinkedBlockingQueue;保證任務由一個執行緒按照指定順序(FIFO, LIFO, 優先順序)序列執行。
示例程式碼如下:
ExecutorService es4 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
es4.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+": "+ index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
輸出:
pool-1-thread-1: 0
pool-1-thread-1: 1
pool-1-thread-1: 2
pool-1-thread-1: 3
pool-1-thread-1: 4
pool-1-thread-1: 5
pool-1-thread-1: 6
pool-1-thread-1: 7
pool-1-thread-1: 8
pool-1-thread-1: 9
同一個執行緒依順序執行