目錄
引言
通過前面的文章,我們學習了Executor框架中的核心類ThreadPoolExecutor ,對於執行緒池的核心排程機制有了一定的瞭解,並且成功使用ThreadPoolExecutor 建立了執行緒池。
而在Java中,除了ThreadPoolExecutor ,Executor框架中還提供了四種執行緒池,這四種執行緒池都是直接或間接配置ThreadPoolExecutor的引數實現的,對於ThreadPoolExecutor類不熟悉的讀者可以參考Java併發程式設計:Java執行緒池核心ThreadPoolExecutor的使用和原理分析
四種執行緒池
四種執行緒池分別是:newCachedThreadPool、newFixedThreadPool 、newScheduledThreadPool 和newSingleThreadExecutor ,下面對這幾個執行緒池一一講解。
newCachedThreadPool:可快取的執行緒池
原始碼:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newCachedThreadPool的方法中是返回一個ThreadPoolExecutor例項,從原始碼中可以看出該執行緒池的特點:
1、該執行緒池的核心執行緒數量是0,執行緒的數量最高可以達到Integer 型別最大值;
2、建立ThreadPoolExecutor例項時傳過去的引數是一個SynchronousQueue例項,說明在建立任務時,若存在空閒執行緒就複用它,沒有的話再新建執行緒。
3、執行緒處於閒置狀態超過60s的話,就會被銷燬。
用法:
public static void main(String[] args) {
//定義ExecutorService例項
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//呼叫execute方法
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + ":" + index);
}
});
}
}
上面的程式碼因為每次迴圈都是隔一秒執行,這個時間足夠之前的執行緒工作完畢,並在新迴圈中複用這個執行緒,程式的執行結果如下:
Thread[pool-1-thread-1,5,main]:0
Thread[pool-1-thread-1,5,main]:1
Thread[pool-1-thread-1,5,main]:2
Thread[pool-1-thread-1,5,main]:3
Thread[pool-1-thread-1,5,main]:4
Thread[pool-1-thread-1,5,main]:5
Thread[pool-1-thread-1,5,main]:6
Thread[pool-1-thread-1,5,main]:7
Thread[pool-1-thread-1,5,main]:8
Thread[pool-1-thread-1,5,main]:9
newFixedThreadPool:定長執行緒池
原始碼:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
執行緒池特點:
1、執行緒池的最大執行緒數等於核心執行緒數,並且執行緒池的執行緒不會因為閒置超時被銷燬。
2、使用的列隊是LinkedBlockingQueue,表示如果當前執行緒數小於核心執行緒數,那麼即使有空閒執行緒也不會複用執行緒去執行任務,而是建立新的執行緒去執行任務。如果當前執行任務數量大於核心執行緒數,此時再提交任務就在佇列中等待,直到有可用執行緒。
用法:
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + ":" + index);
}
});
}
}
定義一個執行緒數為3的執行緒池,迴圈10次執行,可以發現執行的執行緒永遠只有三個,結果如下:
Thread[pool-1-thread-1,5,main]:0
Thread[pool-1-thread-2,5,main]:1
Thread[pool-1-thread-3,5,main]:2
Thread[pool-1-thread-1,5,main]:3
Thread[pool-1-thread-2,5,main]:4
Thread[pool-1-thread-3,5,main]:5
Thread[pool-1-thread-1,5,main]:6
Thread[pool-1-thread-2,5,main]:7
Thread[pool-1-thread-3,5,main]:8
Thread[pool-1-thread-1,5,main]:9
newSingleThreadExecutor:單執行緒執行緒池
原始碼:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
從原始碼就可以看出,該執行緒池基本就是隻有一個執行緒數的newFixedThreadPool,它只有一個執行緒在工作,所有任務按照指定順序執行。
用法:
和newFixedThreadPool類似,只是一直只有一個執行緒在工作,這裡就不貼程式碼了。
newScheduledThreadPool:支援定時的定長執行緒池
原始碼:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
newScheduledThreadPool的方法不是直接返回一個ThreadPoolExecutor例項,而是通過有定時功能的ThreadPoolExecutor,也就是ScheduledThreadPoolExecutor
來返回ThreadPoolExecutor例項,從原始碼中可以看出:
1、該執行緒池可以設定核心執行緒數量,最大執行緒數與newCachedThreadPool一樣,都是Integer.MAX_VALUE。
2、該執行緒池採用的佇列是DelayedWorkQueue,具有延遲和定時的作用。
用法:
public static void main(String[] args) {
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
//延遲3秒執行,只執行一次
((ScheduledExecutorService) scheduledThreadPool).schedule(new Runnable() {
@Override
public void run() {
System.out.println("延遲========");
}
},3,TimeUnit.SECONDS);
//延遲1秒後每隔兩秒執行一次
((ScheduledExecutorService) scheduledThreadPool).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("執行============");
}
},1,2,TimeUnit.SECONDS); //單位是秒
}
自定義ThreadFactory
四種執行緒池的使用就說到這裡了,值得說明的是,除了上面的引數外,Executors類中還給這四種執行緒池提供了可傳ThreadFactory
的過載方法,以下是它們的原始碼:
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
ThreadFactory是一個介面類,也就是我們經常說的執行緒工廠,只有一個方法,可以用於建立執行緒:
Thread newThread(Runnable r);
預設情況下,ThreadPoolExecutor構造器傳入的ThreadFactory
引數是Executors類中的defaultThreadFactory(),相當於一個執行緒工廠,幫我們建立了執行緒池中所需的執行緒。
除此之外,我們也可以自定義ThreadFactory,並根據自己的需要來操作執行緒,下面是例項程式碼:
public static void main(String[] args) {
ExecutorService service = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
System.out.println("我是執行緒" + r);
return t;
}
}
);
//用lambda表示式編寫方法體中的邏輯
Runnable run = () -> {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "正在執行");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
for (int i = 0; i < 5; i++) {
service.submit(run);
}
//這裡一定要做關閉
service.shutdown();
}
執行程式碼後,控制行會輸出五行 “我是執行緒java.util.concurrent.ThreadPoolExecutor。。。。。”的資訊,也證明了我們自定義的ThreadFactory起到了作用。