線程池是用來(lái)統(tǒng)一管理線程的,在 Java 中創(chuàng)建和銷毀線程都是一件消耗資源的事情,線程池可以重復(fù)使用線程,不再頻繁的創(chuàng)建、銷毀線程。
初識(shí)
Java 中的線程池是由 juc 即 java.util.concurrent 包來(lái)實(shí)現(xiàn)的,最主要的就是 ThreadPoolExecutor 類。
- Executor: 代表線程池的接口,有一個(gè) execute() 方法,給一個(gè) Runnable 類型對(duì)象就可以分配一個(gè)線程執(zhí)行。
- ExecutorService:是 Executor 的子接口,提供了線程池的一些生命周期方法。代表了一個(gè)線程池管理器。
- ThreadPoolExecutor:一個(gè)線程池的實(shí)現(xiàn)類,可以通過(guò)調(diào)用 Executors 靜態(tài)工廠方法來(lái)創(chuàng)建線程池并返回一個(gè) ExecutorService 對(duì)象。
ThredadPoolExcutor
看一下最常用的 ThredadPoolExcutor ,下圖是 ThreadPoolExecutor 的構(gòu)造函數(shù)
從源碼中可以看出每個(gè)前三個(gè)構(gòu)造函數(shù)都調(diào)用了最后一個(gè)構(gòu)造函數(shù)。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue< Runnable > workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//省略代碼
}
仔細(xì)分析一下構(gòu)造參數(shù):
- corePoolSize:線程池里的核心線程數(shù)量,當(dāng)正在運(yùn)行的線程數(shù)量小于核心線程數(shù)量,就創(chuàng)建一個(gè)核心線程。
- maximumPoolSize:線程池最多能放多少個(gè)線程。
- keepAliveTime:線程的閑置時(shí)間,當(dāng)線程池里面的線程數(shù)量大于 corePoolSize 的時(shí)候,多出來(lái)的線程在等待的時(shí)間之后會(huì)被釋放掉
- unit:keepAliveTime 的單位
- workQueue:一個(gè)阻塞隊(duì)列。
- threadFactory:通過(guò)這個(gè)工廠模式創(chuàng)建線程。
- handler:處理線程隊(duì)列滿了報(bào)錯(cuò)的。
結(jié)合線程池的參數(shù)簡(jiǎn)單的畫(huà)出線程池的工作模型。
當(dāng)線程池中的核心線程數(shù)量 corePoolSize 滿了,就會(huì)將任務(wù)先加入到任務(wù)隊(duì)列 workQueue 中。
執(zhí)行過(guò)程
線程池的執(zhí)行過(guò)程如下圖:
- 首先判斷核心線程 corePoolSize 是不是滿了,如果沒(méi)有滿,就執(zhí)行任務(wù),否則就進(jìn)入下一步。
- 線程池判斷任務(wù)隊(duì)列 workQueue 是否了,如果沒(méi)有滿,則將新提交的任務(wù)放入在這個(gè)任務(wù)隊(duì)列里。如果任務(wù)隊(duì)列滿了,則進(jìn)入一步。
- 判斷線程池里的線程達(dá)到了最大線程數(shù) maximumPoolSize,如果沒(méi)有,則創(chuàng)建一個(gè)新的線程來(lái)執(zhí)行任務(wù)。如果已經(jīng)滿了,則交給拒絕策略來(lái)處理這個(gè)任務(wù)。
常用的線程池
線程池的創(chuàng)建需要有 7 個(gè)參數(shù),還是比較復(fù)雜的,JVM 為我們提供了 Executors 類中多個(gè)靜態(tài)工廠,生成一些常用的線程池。
SingleThreadExecutor
單線程的線程池,里面就一個(gè)核心線程數(shù)。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue< Runnable >()));
}
ThreadPoolExecutor 參數(shù)只有一個(gè)核心線程數(shù)和一個(gè)最大線程數(shù),這個(gè)很少用到。它保證了所有線程的執(zhí)行順序都是按照提交到線程池的順序執(zhí)行。
public class TodoDemo implements Runnable {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i = 0; i < 10; i++) {
executorService.execute(new TodoDemo());
}
executorService.shutdown();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Running");
}
}
只有一個(gè)線程在跑。
FixedThreadExecutor
固定數(shù)量的線程池
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue< Runnable >());
}
這個(gè)線程池的特點(diǎn)就是線程的數(shù)量是固定的,超過(guò)這個(gè)數(shù)量的任務(wù)就得在 LinkedBlockingQueue 中排隊(duì)等候。
public class TodoDemo implements Runnable {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for(int i = 0; i < 10; i++) {
executorService.execute(new TodoDemo());
}
executorService.shutdown();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Running");
}
}
可以看到就算提交 100 個(gè)任務(wù)也只有 3 個(gè)線程。
CachedThreadExecutor
自動(dòng)回收空閑的線程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue< Runnable >());
}
可以看到核心線程數(shù)量為 0, 表示不會(huì)永久保留任何的線程,最大線程的數(shù)量是 Integer.MAX_VALUE,可以無(wú)限制的創(chuàng)建線程,但是當(dāng)有大量線程處于空閑狀態(tài)的時(shí)候,超過(guò) 60s 就會(huì)被銷毀。
public class TodoDemo implements Runnable {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 0; i < 20; i++) {
executorService.execute(new TodoDemo());
}
executorService.shutdown();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Running");
}
}
雖然這個(gè)線程池可以想建多少個(gè)線程就建多少個(gè)線程,但是還是會(huì)重用已經(jīng)完成任務(wù)的線程。
一般最常用的是FixedThreadExecutor和CachedThreadExecutor。
線程池的回收策略
在線程池中任務(wù)隊(duì)列已經(jīng)滿了,并且線程的數(shù)量已經(jīng)到了最大的數(shù)量,這個(gè)時(shí)候再加任務(wù)線程池就不再接受了。
在 ThreadPoolExecutor 里有 4 種拒絕策略,都實(shí)現(xiàn)了 RejectedExecutionHandler:
- AbortPolicy 表示拋出一個(gè)異常。
- DiscardPolicy 拒絕任務(wù)但是不提示。
- DiscardOldestPolicy 丟棄掉老的任務(wù),執(zhí)行新的任務(wù)。
- CallerRunsPolicy 直接調(diào)用線程處理。
總結(jié)
線程池的作用是提高系統(tǒng)的性能和線程的利用率,不再需要頻繁的創(chuàng)建和銷毀線程。如果使用最簡(jiǎn)單的方式創(chuàng)建線程,在用戶量巨大的情況下,消耗的性能是非常恐怖的,所以才有了線程池。
-
接口
+關(guān)注
關(guān)注
33文章
8496瀏覽量
150834 -
JAVA
+關(guān)注
關(guān)注
19文章
2957瀏覽量
104544 -
管理器
+關(guān)注
關(guān)注
0文章
242瀏覽量
18489 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4304瀏覽量
62427 -
線程池
+關(guān)注
關(guān)注
0文章
57瀏覽量
6834
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論