線程池

    看了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;
        }

“ThreadPoolExecutor 將根據 corePoolSize和 maximumPoolSize(參見 設置的邊界自動調整池大小。當新任務在方法 execute(java.lang.Runnable) 中提交時,如果運行的線程少於 corePoolSize,則創建新線程來處理請求,即使其他輔助線程是空閒的。如果運行的線程多於 corePoolSize 而少於 maximumPoolSize,則僅當隊列滿時才創建新線程。(最開始沒有理解,因爲又把線程和任務混淆了,以爲運行的線程數就是任務數,汗,隊列滿這三個字才與任務有關的了,也就是運行的任務數與線程數相同,但是又有新任務來的時候,這個時候就要new一個線程了如果設置的 corePoolSize 和 maximumPoolSize 相同,則創建了固定大小的線程池。如果將 maximumPoolSize 設置爲基本的***值(如 Integer.MAX_VALUE),則允許池適應任意數量的併發任務。在大多數情況下,核心和最大池大小僅基於構造來設置,不過也可以使用 setCorePoolSize(int)setMaximumPoolSize(int) 進行動態更改”

“keepAliveTime保持活動時間
  如果池中當前有多於 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");
                }
        }
}
上面創建了兩個線程池,下面是執行任務時的代碼
criticalPool.execute(new Runnable() {

                                                public void run() {
                                                        handler.handle(task);
                                                }
                                        });
commonPool.execute(new Runnable() {

                                        public void run() {
                                                handler.handle(task);
                                        }
                                });

ok,至此,終於看明白任務分配這部分代碼了。加油,may!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章