Java中常用的四種執行緒池
在Java中使用執行緒池,可以用ThreadPoolExecutor的建構函式直接創建出執行緒池例項,如何使用參見之前的文章Java執行緒池構造引數詳解。不過,在Executors類中,為我們提供了常用執行緒池的建立方法。接下來我們就來了解常用的四種:
newFixedThreadPool
首先,看一下這種執行緒池的建立方法:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
從構造方法可以看出,它建立了一個固定大小的執行緒池,每次提交一個任務就建立一個執行緒,直到執行緒達到執行緒池的最大值nThreads
。執行緒池的大小一旦達到最大值後,再有新的任務提交時則放入無界阻塞佇列中,等到有執行緒空閒時,再從佇列中取出任務繼續執行。
那麼,如何使用newFixedThreadPool
呢?我們來舉個例子:
public class OneMoreStudy { public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { try { SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm:ss"); System.out.println("執行時間: " + sdf.format(new Date()) + " " + index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } fixedThreadPool.shutdown(); } }
上面的例子中建立了一個固定大小為3的執行緒池,然後線上程池提交了5個任務。在提交第4個任務時,因為執行緒池的大小已經達到了3並且前3個任務在執行中,所以第4個任務被放入了佇列,等待有空閒的執行緒時再被執行。執行結果如下(注意前3個任務和後2個任務的執行時間):
執行時間: 08:09:02 1
執行時間: 08:09:02 2
執行時間: 08:09:02 0
執行時間: 08:09:04 4
執行時間: 08:09:04 3
newCachedThreadPool
首先,看一下這種執行緒池的建立方法:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
從構造方法可以看出,它建立了一個可快取的執行緒池。當有新的任務提交時,有空閒執行緒則直接處理任務,沒有空閒執行緒則建立新的執行緒處理任務,佇列中不儲存任務。執行緒池不對執行緒池大小做限制,執行緒池大小完全依賴於作業系統(或者說JVM)能夠建立的最大執行緒大小。如果執行緒空閒時間超過了60秒就會被回收。
那麼,如何使用newCachedThreadPool
呢?我們來舉個例子:
public class OneMoreStudy {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
final int index = i;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat(
"HH:mm:ss");
System.out.println("執行時間: " +
sdf.format(new Date()) + " " + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
cachedThreadPool.shutdown();
}
}
因為這種執行緒有新的任務提交,就會建立新的執行緒(執行緒池中沒有空閒執行緒時),不需要等待,所以提交的5個任務的執行時間是一樣的,執行結果如下:
執行時間: 08:45:18 2
執行時間: 08:45:18 1
執行時間: 08:45:18 3
執行時間: 08:45:18 4
執行時間: 08:45:18 0
newSingleThreadExecutor
首先,看一下這種執行緒池的建立方法:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
從構造方法可以看出,它建立了一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序執行。
那麼,如何使用newSingleThreadExecutor
呢?我們來舉個例子:
public class OneMoreStudy {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat(
"HH:mm:ss");
System.out.println("執行時間: " +
sdf.format(new Date()) + " " + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
singleThreadExecutor.shutdown();
}
}
因為該執行緒池類似於單執行緒執行,所以先執行完前一個任務後,再順序執行下一個任務,
執行結果如下:
執行時間: 08:54:17 0
執行時間: 08:54:19 1
執行時間: 08:54:21 2
執行時間: 08:54:23 3
執行時間: 08:54:25 4
有的同學可能會質疑:既然類似於單執行緒執行,那麼這種執行緒池還有存在的必要嗎?這裡的單執行緒執行指的是執行緒池內部,從執行緒池外的角度看,主執行緒在提交任務到執行緒池時並沒有阻塞,仍然是非同步的。
newScheduledThreadPool
這個方法建立了一個固定大小的執行緒池,支援定時及週期性任務執行。
首先看一下定時執行的例子:
public class OneMoreStudy {
public static void main(String[] args) {
final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
System.out.println("提交時間: " + sdf.format(new Date()));
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("執行時間: " + sdf.format(new Date()));
}
}, 3, TimeUnit.SECONDS);
scheduledThreadPool.shutdown();
}
}
使用該執行緒池的schedule
方法,延遲3秒鐘後執行任務,執行結果如下:
提交時間: 09:11:39
執行時間: 09:11:42
再看一下週期執行的例子:
public class OneMoreStudy {
public static void main(String[] args) {
final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
System.out.println("提交時間: " + sdf.format(new Date()));
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("執行時間: " + sdf.format(new Date()));
}
}, 1, 3, TimeUnit.SECONDS);
Thread.sleep(10000);
scheduledThreadPool.shutdown();
}
}
使用該執行緒池的scheduleAtFixedRate
方法,延遲1秒鐘後每隔3秒執行一次任務,執行結果如下:
提交時間: 09:23:20
執行時間: 09:23:21
執行時間: 09:23:24
執行時間: 09:23:27