1. 程式人生 > >Java Thread Pool

Java Thread Pool

Java通過Executors提供四種執行緒池,分別為:

  • newCachedThreadPool: 建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
  • newFixedThreadPool: 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
  • newScheduledThreadPool: 建立一個定長執行緒池,支援定時及週期性任務執行。
  • newSingleThreadExecutor: 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

1. newCachedThreadPool

建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。示例程式碼如下:

ExecutorService chacheThreadPool = Executors.newCachedThreadPool();
      for(int i = 0; i < 10; i++) {
           final int index = i;
           try{
                Thread.sleep(index * 1000);
           } catch (InterruptedException e)
{ e.printStackTrace(); } chacheThreadPool.execute(new Runnable() { @Override public void run() { // TODO Auto-generated method stub System.out.println(index); } }); }

執行緒池為無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的執行緒,而不用每次新建執行緒。

2. newFixedThreadPool

建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。示例程式碼如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
  for (int i = 0; i < 10; i++) {
      final int index = i;
      fixedThreadPool.execute(new Runnable() {
   
          @Override
          public void run() {
              try {
                  System.out.println(index);
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          }
      });
  }

因為執行緒池大小為3,每個任務輸出index後sleep 2秒,所以每兩秒列印3個數字。 定長執行緒池的大小最好根據系統資源進行設定。如Runtime.getRuntime().availableProcessors()。

3. newScheduledThreadPool

建立一個定長執行緒池,支援定時及週期性任務執行。延遲執行示例程式碼如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
  scheduledThreadPool.schedule(new Runnable() {
   
      @Override
      public void run() {
       System.out.println("delay 3 seconds");
      }
  }, 3, TimeUnit.SECONDS);

表示延遲3秒執行。

定期執行示例程式碼如下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
 
    @Override
    public void run() {
        System.out.println("delay 1 seconds, and excute every 3 seconds");
    }
}, 1, 3, TimeUnit.SECONDS);

表示延遲1秒後每3秒執行一次。

ScheduledExecutorService比Timer更安全,功能更強大,後面會有一篇單獨進行對比。

4. newSingleThreadExecutor

建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。示例程式碼如下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  for (int i = 0; i < 10; i++) {
      final int index = i;
      singleThreadExecutor.execute(new Runnable() {
   
          @Override
          public void run() {
              try {
                  System.out.println(index);
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          }
      });
  }

結果依次輸出,相當於順序執行各個任務。

現行大多數GUI程式都是單執行緒的。Android中單執行緒可用於資料庫操作,檔案操作,應用批量安裝,應用批量刪除等不適合併發但可能IO阻塞性及影響UI執行緒響應的操作。

綜上所述的4中方式,主要有以下幾種見解:

  1. 第一種方式跟第四種方式,運行了之後,發現結果是一樣的,那是不是說這兩種執行緒池的使用是一樣的呢?如果是一樣的,為什麼會有兩種而不是一種?其實大家仔細看下一開始這兩個方法的介紹就知道了,第一種:newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。這裡覺得它可能是併發且是無序的。第四種:newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。是一個單執行緒的執行緒池,個人覺得它是有序的,因為有優先順序。

  2. 第二種方式,可指定執行緒池的大小,並且每隔一段時間執行多少資料。比如上面的例子就是:因為執行緒池大小為3,每個任務輸出index後sleep 2秒,所以每兩秒列印3個數字。相信大家都能夠理解了。

  3. 第三種方式就更簡單了,這裡介紹了兩種情況,一種是隻執行一次的情況,一種是延遲幾秒再每隔幾秒執行一次。可能這樣說大家不太明白,我舉個簡單的例子。比如現在很多應用的首頁有一個廣告部分,每隔幾秒後就會自動播放下一張圖片,這裡用的執行緒就是此執行緒。其實很多Android開發遇到這種情況,都會首先想到用定時器(Timer),其實不然,很多帖子都說明了,這種方式比用Timer更好。