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中方式,主要有以下幾種見解:
-
第一種方式跟第四種方式,運行了之後,發現結果是一樣的,那是不是說這兩種執行緒池的使用是一樣的呢?如果是一樣的,為什麼會有兩種而不是一種?其實大家仔細看下一開始這兩個方法的介紹就知道了,第一種:newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。這裡覺得它可能是併發且是無序的。第四種:newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。是一個單執行緒的執行緒池,個人覺得它是有序的,因為有優先順序。
-
第二種方式,可指定執行緒池的大小,並且每隔一段時間執行多少資料。比如上面的例子就是:因為執行緒池大小為3,每個任務輸出index後sleep 2秒,所以每兩秒列印3個數字。相信大家都能夠理解了。
-
第三種方式就更簡單了,這裡介紹了兩種情況,一種是隻執行一次的情況,一種是延遲幾秒再每隔幾秒執行一次。可能這樣說大家不太明白,我舉個簡單的例子。比如現在很多應用的首頁有一個廣告部分,每隔幾秒後就會自動播放下一張圖片,這裡用的執行緒就是此執行緒。其實很多Android開發遇到這種情況,都會首先想到用定時器(Timer),其實不然,很多帖子都說明了,這種方式比用Timer更好。