基本介紹
線程池,顧名思義,就是一個線程的池子,裏有若干線程,它們的目的就是執行提交給線程池的任務,執行完一個任務後不會退出,而是繼續等待或執行新的任務。
線程池的優點:
- 可以重用線程,避免線程創建的開銷
- 任務過多時,通過排序避免創建過多線程,減少系統資源消耗和競爭,確任務有序完成
ThreadPoolExecutor
Java併發包中線程池的實現類是ThreadPollExecutor,它繼承了AbstractExecutorService,AbstractExecutorService實現了ExecutorService
我們先看看ThreadPoolExecutor的主要構造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
我們先談談這些參數
線程池大小
- corePoolSize
- maximumPoolSize
- keepAliveTime 和 unit
corePollSize表示線程池中的核心線程個數,並不是一開始就創建這麼多線程,默認情況下,剛創建一個線程池後並不會創建任何線程。線程個數小於等於corePoolSize時,我們稱這些線程爲核心線程
maximumPoolSize表示線程池中的最多線程數,線程的個數會動態變化,但都不會創建比這個值大的線程個數
一般情況下,新任務到來的時候,如果當前線程個數小於corePoolSize,就會創建一個新線程來執行該任務,即使其他線程現在也是空閒的。如果線程個數大於等於corePoolSize,那就不會立即創建新線程,而是先嚐試排隊,如果隊列滿了或其他原因不能立即入隊,它就不會排隊,而是檢查線程個數是否達到了maximumPoolSize,如果沒有,就會創建線程。
keepAliveTime表示當線程池中線程個數大於corePoolSize時額外空閒線程的存活時間。也就是說,一個非核心線程,在空閒等待新任務時,會有一個最長等待時間(keepAliveTime),如果到了時間還沒有新任務,就會被終止。如果該值爲0,則表示所有線程都不會超時終止。
默認情況下:核心線程不會預先創建,只有當有任務時纔會創建;核心線程不會因爲空閒而被終止。
不過ThreadPoolExecutor可以改變這個默認行爲
// 預先創建所有的核心線程
public int prestartAllCoreThreads();
// 創建一個核心線程,如果所有核心線程都已創建,返回false
public boolean prestartCoreThread();
// 如果設置爲true,則keepAliveTime參數也適用於核心線程
public void allowCoreThreadTimeout(boolean value);
隊列
ThreadPoolExecutor要求隊列類型是阻塞隊列BlockingQueue。
在介紹併發容器的時候瞭解過幾種阻塞隊列:
- LinkedBlockingQueue:基於鏈表的阻塞隊列,可以指定最大長度,但默認是無界的
- ArrayBlockingQueue:基於數組的有界阻塞隊列
- PriorityBlockingQueue:基於堆的無界阻塞隊列
- SynchronousQueue:沒有實際存儲空間的同步阻塞隊列
拒絕策略
如果隊列有界,且maximumPoolSize有限,則當隊列排滿,線程個數也達到了maximumPoolSize時,新任務來了,就會觸發線程池的任務拒絕策略
ThreadPoolExecutor實現了4種處理方法:
1. ThreadPoolExecutor.AbortPolicy:這是默認的方式,拋出異常RejectedExecutionException
2. ThreadPoolExecutor.DiscardPolicy:忽略新任務,不拋出異常,也不執行
3. ThreadPoolExecutor.DiscardOldestPolicy:將等待時間最長的任務扔掉,然後排隊
4. ThreadPoolExecutor.CallerRunsPolicy:在任務提交者線程中執行任務,而不是交給線程池中的線程執行
它們都是ThreadPoolExecutor的內部類,實現了RejectedExecutionHandler接口
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
線程工廠
還有一個參數:ThreadFactory。它是一個接口
public interface ThreadFactory {
Thread newThread(Runnable r);
}
這個接口根據Runnable創建一個Thread。
ThreadPoolExecutor的默認實現是Executors類中的靜態內部類DefaultThreadFacotry,主要就是創建一個線程,給線程設置名字,設置daemon屬性爲false,設置線程優先級爲標準默認優先級,線程名字格式爲:pool-<線程池編號>-thread-<線程編號>
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
工廠類Executors
類Executors提供了一些靜態工程方法,可以方便地創建一些預配置的線程池
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
只使用一個線程,使用無界的LinkedBlockingQueue,線程創建後不會超時終止,該線程順序執行所有任務。該線程適用於需要確保所有任務被順序執行的場合
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
使用固定數目的n個線程,使用無界LinkedBlockingQueue,線程創建後不會超時終止,由於是無界隊列,如果排隊任務過多,會消耗過多的內存。
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是60秒,隊列爲SynchronousQueue。
當新任務來的時候,如果正好有空閒線程在等待任務,則其中一個空閒線程接受該任務,否則就總是創建一個新的線程,創建的總線程個數不受限制,對於空閒線程,如果60秒內沒有新任務就終止
線程池的死鎖
提交給線程池的任務,如果任務之間有依賴,可能會出現死鎖。
如果任務A提交給了一個單線程線程池,在它的執行過程中,它給同樣的任務執行服務提交了一個任務B,但需要等待任務B返回結果,這樣就會出現死鎖。
處理辦法:使用newCachedThreadPool創建線程池,讓線程數不受限制;使用SynchronousQueue作爲等待隊列,對於SynchronousQueue來說,入隊成功就意味着已有線程接受處理,如果入隊失敗,可以創建新的線程