1. 程式人生 > >java執行緒池以及newCachedThreadPool使用過程中的問題

java執行緒池以及newCachedThreadPool使用過程中的問題

      為什麼要用執行緒池?原因很簡單,效能好,而且不用自己費心費力的管理執行緒

      1、執行緒池基本說明及定義

      從JDK 1.5開始,添加了Executors工具類,這個類定義了Executor、ExecutorService、ScheduledExecutorService、ThreadFactory、Calable類的工廠和實用方法。在Executors類的原始碼註釋中,這樣寫道:

      該類包含:

      1. 提供能夠建立並返回設定了常用配置的ExecutorService的方法
      2. 提供能夠建立並返回設定了常用配置的ScheduledExecutorService的方法
      3. 提供能夠建立並返回一個經過包裝(禁用了特定的實現方法,僅僅暴露ExecutorService的實現)的ExecutorService方法
      4. 提供能夠建立並返回能修改新建執行緒狀態的ThreadFactory的方法
      5. 提供能夠建立並返回非閉包形式的Callable,因此能夠在需要Callable的執行方法中使用
      Executors類提供4種執行緒池:

      1. newFixedThreadPool:建立固定大小

的執行緒池

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
 }
 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
 }
      相比下面將要介紹的newCachedThreadPool,newFixedThreadPool可控制執行緒最大併發數,當執行緒池中的執行緒數達到其設定大小時,其餘新建立的執行緒會在LinkedBlockingQueue佇列中等待。當執行緒池中的某個執行緒失敗而終止時,新的執行緒會代替它執行剩下的任務。執行緒池中的執行緒只有在顯式呼叫shutdown函式時才會退出執行緒池       2. newCachedThreadPool:建立可快取執行緒池,當執行緒池中的執行緒空閒時間超過60s,便會終止該空閒執行緒並從快取執行緒池中移除
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
 }

      該執行緒池可根據需要,建立新執行緒,從上面程式碼中可以看出,其執行緒數最大可為Integer.MAX_VALUE,如果控制不好,使用該執行緒池可能導致執行緒建立過多,反而影響效能。因此,可以注意一下該執行緒池的使用場景:對於大量短暫非同步任務的程式來說,使用該執行緒池能夠大大提高效能 

      3. newSingleThreadExecutor,建立單執行緒的執行緒池     

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
 }
 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
 }
      當該單執行緒在shutdown之前由於失敗而終止時,將會有新的執行緒來代替它執行剩下任務。加入到該執行緒池裡的執行緒會按順序執行,一個時刻保證只有一個執行緒在執行       4. newScheduledThreadPool,建立固定大小且能夠執行定時或週期性任務的執行緒池  
 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
 }
 public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
 }

      2、執行緒池使用

      使用過程很簡單,下面以newFixedThreadPool為例:建立一個大小為3的一個執行緒池,一共10個執行緒任務,每個任務會輸出當前執行執行緒的名稱,然後睡眠500ms(睡眠的目的是使結果更清晰)
public class TestNewFixedThreadPool {
    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; ++i) {
            newFixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

      結果:

pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-3

      從輸出結果可以看出,線上程池中一共有3個執行緒,原因是該執行緒池控制了最大併發執行緒數為3。當使用newCachedThreadPool時,由於存在睡眠時長,很有可能會有10個執行緒來執行

      3、newCachedThreadPool使用過程中遇到的問題

      在寫程式碼的時候,遇到一個問題,大約有300多個任務。剛開始使用的執行緒池為newCachedThreadPool,並設定最大阻塞時長60s。執行時發現,每次都要等到阻塞時間到達後被強制停止,才會執行之後的程式。後來改為newFixedThreadPool(10)來執行,就執行正常了。感覺很奇怪。通過日誌列印,看到當使用newCachedThreadPool執行緒池的時候,會大約建立300多個執行緒,隨著任務的增加,執行緒數也會增加,導致系統資源被大量佔用,有的執行緒被阻塞住了,到設定到的最大阻塞時長時,任務被強制停止。因此,使用newCachedThreadPool的時候要根據實際任務執行時間情況,因為它可以建立最多Integer.MAX_VALUE個執行緒,反而會佔用系統資源,降低執行效率。這就是為什麼官方文件中會說:newCachedThreadPool會大幅度提高大量短暫非同步任務的效能了