看了3天代碼,遇到很多困難,差點要堅持不下去了,但是我覺得還是有一定的收穫的,所以一定要繼續努力!
這兩天被線程池給整暈了,今天去找jh給我劃一下Java核心技術裏哪些重點看,哪些可以不看的,順便讓他給我講解了一下線程池,我要記錄下來,這樣才能成長!
首先,我之前的一個理解,有線程就一定有任務,線程和任務是分不開的,這樣導致我在看到線程池這個概念的時候就很暈。現在看來,線程和任務其實是獨立開的。
先看一下java.util.concurrent中的ThreadPoolExecutor類,JDK API 1.6中說明:“一個 ExecutorService
,它使用可能的幾個池線程之一執行每個提交的任務,通常使用
Executors
工廠方法配置。
線程池可以解決兩個不同問題:由於減少了每個任務調用的開銷,它們通常可以在執行大量異步任務時提供增強的性能,並且還可以提供綁定和管理資源(包括執行任務集時使用的線程)的方法。每個
ThreadPoolExecutor 還維護着一些基本的統計數據,如完成的任務數。 ”
稍微有點抽象,線程池可以看做一個Queue,裏面存着一些可以使用的線程,這些線程是一直存在的,因爲每次任務來的時候就分配一個線程給它去執行,這樣就節省了線程的new和銷燬的動作,會提升性能(這個應該是上面講的線程池解決的第一個問題)。那麼這些可使用的線程有多少呢?看一下這個構造函數:
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;
}
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;
}
“ThreadPoolExecutor 將根據 corePoolSize和 maximumPoolSize(參見
設置的邊界自動調整池大小。當新任務在方法
execute(java.lang.Runnable)
中提交時,如果運行的線程少於 corePoolSize,則創建新線程來處理請求,即使其他輔助線程是空閒的。如果運行的線程多於 corePoolSize 而少於
maximumPoolSize,則僅當隊列滿時才創建新線程。(最開始沒有理解,因爲又把線程和任務混淆了,以爲運行的線程數就是任務數,汗,隊列滿這三個字才與任務有關的了,也就是運行的任務數與線程數相同,但是又有新任務來的時候,這個時候就要new一個線程了)如果設置的 corePoolSize 和 maximumPoolSize
相同,則創建了固定大小的線程池。如果將 maximumPoolSize 設置爲基本的***值(如
Integer.MAX_VALUE),則允許池適應任意數量的併發任務。在大多數情況下,核心和最大池大小僅基於構造來設置,不過也可以使用 setCorePoolSize(int)
和 setMaximumPoolSize(int)
進行動態更改”如果池中當前有多於 corePoolSize 的線程,則這些多出的線程在空閒時間超過 keepAliveTime 時將會終止(core和普通的就是不一樣啊)。這提供了當池處於非活動狀態時減少資源消耗的方法。如果池後來變得更爲活動,則可以創建新的線程。也可以使用方法
setKeepAliveTime(long,
java.util.concurrent.TimeUnit)
動態地更改此參數。使用 Long.MAX_VALUE TimeUnit.NANOSECONDS
的值在關閉前有效地從以前的終止狀態禁用空閒線程。默認情況下,保持活動策略只在有多於 corePoolSizeThreads 的線程時應用。但是隻要
keepAliveTime 值非 0,allowCoreThreadTimeOut(boolean)
方法也可將此超時策略應用於核心線程。 ”“排隊(三種策略不寫了)
所有
BlockingQueue
都可用於傳輸和保持提交的任務。可以使用此隊列與池大小進行交互:- 如果運行的線程少於 corePoolSize,則 Executor 始終首選添加新的線程,而不進行排隊
- 如果運行的線程等於或多於 corePoolSize,則 Executor 始終首選將請求加入隊列,而不添加新的線程。
- 如果無法將請求加入隊列,則創建新的線程,除非創建此線程超出 maximumPoolSize,在這種情況下,任務將被拒絕。 ”
另外線程池佔用內存大小,可以從jvm的內存配置中每個線程棧大小的配置中估算:-Xss256k。
下面是這兩天看的項目代碼:
public class TaskDispatcher {
......
public void init() {
// 初始化線程池
criticalPool = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveTime,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(this.queueSize));
commonPool = new ThreadPoolExecutor(0, this.maxPoolSize / 2, this.keepAliveTime, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(this.queueSize));
// 設置線程池可用的標誌
criticalPoolValid = true;
if (log.isInfoEnabled()) {
log.info("TaskDispatcher initialized");
}
}
}
......
public void init() {
// 初始化線程池
criticalPool = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveTime,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(this.queueSize));
commonPool = new ThreadPoolExecutor(0, this.maxPoolSize / 2, this.keepAliveTime, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(this.queueSize));
// 設置線程池可用的標誌
criticalPoolValid = true;
if (log.isInfoEnabled()) {
log.info("TaskDispatcher initialized");
}
}
}
criticalPool.execute(new Runnable() {
public void run() {
handler.handle(task);
}
});
public void run() {
handler.handle(task);
}
});
commonPool.execute(new Runnable() {
public void run() {
handler.handle(task);
}
});
public void run() {
handler.handle(task);
}
});
ok,至此,終於看明白任務分配這部分代碼了。加油,may!