為什么用線程池,在Java中創(chuàng)建線程無非。就是new Thread和實現(xiàn)Runable接口來創(chuàng)建線程,但是這種方法如果不去控制數(shù)量,一味的去new 的話是很損耗系統(tǒng)資源的。所以對于線程的優(yōu)化就有了線程池的出現(xiàn),它是可以把線程歸納到一個線程池統(tǒng)一管理和使用。
Java線程池的核心就是使用ThreadPoolExecutor類看看它的構造方法有什么參數(shù)
public ThreadPoolExecutor(int corePoolSize, 線程池中核心線程數(shù)量
int maximumPoolSize, 最大線程數(shù)量
long keepAliveTime, 保存活動的時間,不過它起作用必須在一個前提下,就是當線程池中的線程數(shù)量超過了corePoolSize時,它表示多余的空閑線程的存活時間,
即:多余的空閑線程在超過keepAliveTime時間內沒有任務的話則被銷毀。而這個主要應用在緩存線程池中
TimeUnit unit, 表示keepAliveTime的單位,常用的如:TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)
BlockingQueue<Runnable> workQueue, 任務隊列,主要用來存儲已經(jīng)提交但未被執(zhí)行的任務,不同的線程池采用的排隊策略不一樣
ThreadFactory threadFactory, 線程工廠,用來創(chuàng)建線程池中的線程,通常用默認的即可
RejectedExecutionHandler handler 通常叫做拒絕策略,1、在線程池已經(jīng)關閉的情況下 2、任務太多導致最大線程數(shù)和任務隊列已經(jīng)飽和,無法再接收新的任務 。在上面兩種情況下,只要滿足其中一種時,
在使用execute()來提交新的任務時將會拒絕,而默認的拒絕策略是拋一個RejectedExecutionException異常
) {
Executors提供五種隊列對應的Queue類型
newFixedThreadPool()--LinkedBlockingQueue
newSingleThreadExecutor()--LinkedBlockingQueue
newCachedThreadPool()--SynchronousQueue
newScheduledThreadPool()--DelayedWorkQueue
newSingleThreadScheduledExecutor()--DelayedWorkQueue
LinkedBlockingQueue:無界的隊列
SynchronousQueue:直接提交的隊列
DelayedWorkQueue:等待隊列
五種隊列的作用
newFixedThreadPool() :
作用:該方法返回一個固定線程數(shù)量的線程池,該線程池中的線程數(shù)量始終不變,即不會再創(chuàng)建新的線程,也不會銷毀已經(jīng)創(chuàng)建好的線程,自始自終都是那幾個固定的線程在工作,所以該線程池可以控制線程的最大并發(fā)數(shù)。
栗子:假如有一個新任務提交時,線程池中如果有空閑的線程則立即使用空閑線程來處理任務,如果沒有,則會把這個新任務存在一個任務隊列中,一旦有線程空閑了,則按FIFO方式處理任務隊列中的任務。newCachedThreadPool() :
作用:該方法返回一個可以根據(jù)實際情況調整線程池中線程的數(shù)量的線程池。即該線程池中的線程數(shù)量不確定,是根據(jù)實際情況動態(tài)調整的。
栗子:假如該線程池中的所有線程都正在工作,而此時有新任務提交,那么將會創(chuàng)建新的線程去處理該任務,而此時假如之前有一些線程完成了任務,現(xiàn)在又有新任務提交,那么將不會創(chuàng)建新線程去處理,而是復用空閑的線程去處理新任務。那么此時有人有疑問了,那這樣來說該線程池的線程豈不是會越集越多?其實并不會,因為線程池中的線程都有一個“保持活動時間”的參數(shù),通過配置它,如果線程池中的空閑線程的空閑時間超過該“保存活動時間”則立刻停止該線程,而該線程池默認的“保持活動時間”為60s。newSingleThreadExecutor() :
作用:該方法返回一個只有一個線程的線程池,即每次只能執(zhí)行一個線程任務,多余的任務會保存到一個任務隊列中,等待這一個線程空閑,當這個線程空閑了再按FIFO方式順序執(zhí)行任務隊列中的任務。newScheduledThreadPool() :
作用:該方法返回一個可以控制線程池內線程定時或周期性執(zhí)行某任務的線程池。newSingleThreadScheduledExecutor() :
作用:該方法返回一個可以控制線程池內線程定時或周期性執(zhí)行某任務的線程池。只不過和上面的區(qū)別是該線程池大小為1,而上面的可以指定線程池的大小。
- 固定線程數(shù),如果超過了設置的數(shù)量,后續(xù)將不會繼續(xù)創(chuàng)建線程而是FIFO方式去處理隊列。
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + " 線程正在執(zhí)行第" + finalI + "個任務");
}
});
}
- 這個線程池就存在一個線程實例,不管有多少個都是走隊列處理
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService1.execute(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + " 線程正在執(zhí)行第" + finalI + "個任務");
}
});
}
- 這個會根據(jù)線池實際使用情況來創(chuàng)建線程,數(shù)量不能確定。
ExecutorService executorService2 = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int finalI = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService2.execute(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + " 線程正在執(zhí)行第" + finalI + "個任務");
try {
Thread.sleep(finalI *500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
- 可以定時的隊列
ScheduledExecutorService scheduledExecutorService = Executors
.newScheduledThreadPool(3);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName());
}
}, 1, TimeUnit.MILLISECONDS);
ScheduledExecutorService scheduledExecutorService4 = Executors
.newScheduledThreadPool(3);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() );
}
},1,2, TimeUnit.MILLISECONDS);
ScheduledExecutorService scheduledExecutorService3 = Executors
.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() );
}
},1,2,TimeUnit.SECONDS);
優(yōu)化線程池ThreadPoolExecutor
雖說線程池極大改善了系統(tǒng)的性能,不過創(chuàng)建線程池也是需要資源的,所以線程池內線程數(shù)量的大小也會影響系統(tǒng)的性能,大了反而浪費資源,小了反而影響系統(tǒng)的吞吐量,所以我們創(chuàng)建線程池需要把握一個度才能合理的發(fā)揮它的優(yōu)點,通常來說我們要考慮的因素有CPU的數(shù)量、內存的大小、并發(fā)請求的數(shù)量等因素,按需調整。 通常核心線程數(shù)可以設為CPU數(shù)量+1,而最大線程數(shù)可以設為CPU的數(shù)量*2+1。獲取CPU數(shù)量的方法為:
Runtime.getRuntime().availableProcessors();
shutdown()和shutdownNow()的區(qū)別
關于線程池的停止,ExecutorService為我們提供了兩個方法:shutdown和shutdownNow,這兩個方法各有不同,可以根據(jù)實際需求方便的運用,如下:
1.shutdown()方法在終止前允許執(zhí)行以前提交的任務。
2.shutdownNow()方法則是阻止正在任務隊列中等待任務的啟動并試圖停止當前正在執(zhí)行的任務。