java 线程池 使用实例
1 線程池做什么
網(wǎng)絡(luò)請(qǐng)求通常有兩種形式:
第一種,請(qǐng)求不是很頻繁,而且每次連接后會(huì)保持相當(dāng)一段時(shí)間來讀數(shù)據(jù)或者寫數(shù)據(jù),最后斷開,如文件下載,網(wǎng)絡(luò)流媒體等。
另一種形式是請(qǐng)求頻繁,但是連接上以后讀/寫很少量的數(shù)據(jù)就斷開連接。考慮到服務(wù)的并發(fā)問題,如果每個(gè)請(qǐng)求來到以后服務(wù)都為它啟動(dòng)一個(gè)線程,那么這對(duì)服務(wù)的資源可能會(huì)造成很大的浪費(fèi),特別是第二種情況。
因?yàn)橥ǔG闆r下,創(chuàng)建線程是需要一定的耗時(shí)的,設(shè)這個(gè)時(shí)間為T1,而連接后讀/寫服務(wù)的時(shí)間為T2,當(dāng)T1>>T2時(shí),我們就應(yīng)當(dāng)考慮一種策略或者機(jī)制來控制,使得服務(wù)對(duì)于第二種請(qǐng)求方式也能在較低的功耗下完成。
通常,我們可以用線程池來解決這個(gè)問題,首先,在服務(wù)啟動(dòng)的時(shí)候,我們可以啟動(dòng)好幾個(gè)線程,并用一個(gè)容器(如線程池)來管理這些線程。
當(dāng)請(qǐng)求到來時(shí),可以從池中取一個(gè)線程出來,執(zhí)行任務(wù)(通常是對(duì)請(qǐng)求的響應(yīng)),當(dāng)任務(wù)結(jié)束后,再將這個(gè)線程放入池中備用;
如果請(qǐng)求到來而池中沒有空閑的線程,該請(qǐng)求需要排隊(duì)等候。最后,當(dāng)服務(wù)關(guān)閉時(shí)銷毀該池即可。
多線程技術(shù)主要解決處理器單元內(nèi)多個(gè)線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時(shí)間,增加處理器單元的吞吐能力。
假設(shè)一個(gè)服務(wù)器完成一項(xiàng)任務(wù)所需時(shí)間為:T1 創(chuàng)建線程時(shí)間,T2 在線程中執(zhí)行任務(wù)的時(shí)間,T3 銷毀線程時(shí)間。
如果:T1 + T3 遠(yuǎn)大于 T2,則可以采用線程池,以提高服務(wù)器性
線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T3時(shí)間的技術(shù),從而提高服務(wù)器程序性能的
它把T1,T3分別安排在服務(wù)器程序的啟動(dòng)和結(jié)束的時(shí)間段或者一些空閑的時(shí)間段,這樣在服務(wù)器程序處理客戶請(qǐng)求時(shí),不會(huì)有T1,T3的開銷了。
線程池不僅調(diào)整T1,T3產(chǎn)生的時(shí)間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目,看一個(gè)例子:
假設(shè)一個(gè)服務(wù)器一天要處理50000個(gè)請(qǐng)求,并且每個(gè)請(qǐng)求需要一個(gè)單獨(dú)的線程完成。在線程池中,線程數(shù)一般是固定的,所以產(chǎn)生線程總數(shù)不會(huì)超過線程池中線程的數(shù)目,
而如果服務(wù)器不利用線程池來處理這些請(qǐng)求則線程總數(shù)為50000。一般線程池大小是遠(yuǎn)小于50000。
所以利用線程池的服務(wù)器程序不會(huì)為了創(chuàng)建50000而在處理請(qǐng)求時(shí)浪費(fèi)時(shí)間,從而提高效率。
合理利用線程池能夠帶來三個(gè)好處:
第一:降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
但是要做到合理的利用線程池,必須對(duì)其原理了如指掌。
2 線程池的繼承架構(gòu)
程序啟動(dòng)一個(gè)新線程成本是比較高的,因?yàn)樗婕暗揭c操作系統(tǒng)進(jìn)行交互。而使用線程池可以很好的提高性能,尤其是當(dāng)程序中要?jiǎng)?chuàng)建大量生存期很短的線程時(shí),更應(yīng)該考慮使用線程池。
線程池里的每一個(gè)線程代碼結(jié)束后,并不會(huì)死亡,而是再次回到線程池中成為空閑狀態(tài),等待下一個(gè)對(duì)象來使用。
在JDK5之前,我們必須手動(dòng)實(shí)現(xiàn)自己的線程池,從JDK5開始,Java內(nèi)置支持線程池
Java里面線程池的頂級(jí)接口是Executor,但是嚴(yán)格意義上講Executor并不是一個(gè)線程池,而只是一個(gè)執(zhí)行線程的工具。
真正的線程池接口是ExecutorService。下面這張圖完整描述了線程池的類體系結(jié)構(gòu)。
Executor是一個(gè)頂層接口,在它里面只聲明了一個(gè)方法execute(Runnable),返回值為void,參數(shù)為Runnable類型,從字面意思可以理解,就是用來執(zhí)行傳進(jìn)去的任務(wù)的;
然后ExecutorService接口繼承了Executor接口,并聲明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象類AbstractExecutorService實(shí)現(xiàn)了ExecutorService接口,基本實(shí)現(xiàn)了ExecutorService中聲明的所有方法;
然后ThreadPoolExecutor繼承了類AbstractExecutorService。
標(biāo)記一下比較重要的類:
|
ExecutorService: |
真正的線程池接口。 |
|
ScheduledExecutorService |
能和Timer/TimerTask類似,解決那些需要任務(wù)重復(fù)執(zhí)行的問題。 |
|
ThreadPoolExecutor |
ExecutorService的默認(rèn)實(shí)現(xiàn)。 |
|
ScheduledThreadPoolExecutor |
繼承ThreadPoolExecutor的ScheduledExecutorService接口實(shí)現(xiàn),周期性任務(wù)調(diào)度的類實(shí)現(xiàn)。 |
要配置一個(gè)線程池是比較復(fù)雜的,尤其是對(duì)于線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優(yōu)的,
因此在Executors類里面提供了一些靜態(tài)工廠,生成一些常用的線程池。
newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。
如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。
newFixedThreadPool:創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。
線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。
newCachedThreadPool:創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過了處理任務(wù)所需要的線程,那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,
當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來處理任務(wù)。
此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
newScheduledThreadPool:創(chuàng)建一個(gè)大小無限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
這些方法的返回值是ExecutorService對(duì)象,該對(duì)象表示一個(gè)線程池,可以執(zhí)行Runnable對(duì)象或者Callable對(duì)象代表的線程。
它提供了如下方法來提交一個(gè)任務(wù):
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
Callable 與 Runable的相關(guān)內(nèi)容參見:
03 創(chuàng)建線程的第3式 02 如何創(chuàng)建線程 線程并發(fā)與synchornized 3使用線程池步驟及案例線程池的好處:線程池里的每一個(gè)線程代碼結(jié)束后,并不會(huì)死亡,而是再次回到線程池中成為空閑狀態(tài),等待下一個(gè)對(duì)象來使用。
如何實(shí)現(xiàn)線程的代碼呢?
A:創(chuàng)建一個(gè)線程池對(duì)象,控制要?jiǎng)?chuàng)建幾個(gè)線程對(duì)象。
public static ExecutorService newFixedThreadPool(int nThreads)
B:這種線程池的線程可以執(zhí)行:
可以執(zhí)行Runnable對(duì)象或者Callable對(duì)象代表的線程
做一個(gè)類實(shí)現(xiàn)Runnable接口。
C:調(diào)用如下方法即可
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
D:我就要結(jié)束,可以嗎? 可以。
1 package com.jt.thread.demo05;
2
3 import java.util.concurrent.ExecutorService;
4 import java.util.concurrent.Executors;
5
6 class MyRunnable implements Runnable {
7 @Override
8 public void run() {
9 for (int x = 0; x < 100; x++) {
10 System.out.println(Thread.currentThread().getName() + ":" + x);
11 }
12 }
13 }
14
15 public class ExecutorServiceDemo {
16 public static void main(String[] args) {
17 // 創(chuàng)建一個(gè)線程池對(duì)象,控制要?jiǎng)?chuàng)建幾個(gè)線程對(duì)象。
18 // public static ExecutorService newFixedThreadPool(int nThreads)
19 ExecutorService pool = Executors.newFixedThreadPool(2);
20
21 // 可以執(zhí)行Runnable對(duì)象或者Callable對(duì)象代表的線程
22 pool.submit(new MyRunnable());
23 pool.submit(new MyRunnable());
24
25 //結(jié)束線程池
26 pool.shutdown();
27 }
28 }
運(yùn)行結(jié)果:
pool-1-thread-1:0
pool-1-thread-1:1
pool-1-thread-1:2
pool-1-thread-2:0
pool-1-thread-2:1
pool-1-thread-2:2
pool-1-thread-2:3
。。。
說明:
(1 newFixedThreadPool
是固定大小的線程池 有結(jié)果可見 我們指定2 在運(yùn)行時(shí)就只有2個(gè)線程工作
若其中有一個(gè)線程異常 會(huì)有新的線程替代他
(2 shutdown方法有2個(gè)重載:
voidshutdown() 啟動(dòng)一次順序關(guān)閉,等待執(zhí)行以前提交的任務(wù)完成,但不接受新任務(wù)。
List<Runnable> shutdownNow() 試圖立即停止所有正在執(zhí)行的活動(dòng)任務(wù),暫停處理正在等待的任務(wù),并返回等待執(zhí)行的任務(wù)列表。
(3 submit 與 execute
3.1 submit是ExecutorService中的方法 用以提交一個(gè)任務(wù)
他的返回值是future對(duì)象 可以獲取執(zhí)行結(jié)果
<T> Future<T> submit(Callable<T> task) 提交一個(gè)返回值的任務(wù)用于執(zhí)行,返回一個(gè)表示任務(wù)的未決結(jié)果的 Future。
Future<?> submit(Runnable task) 提交一個(gè) Runnable 任務(wù)用于執(zhí)行,并返回一個(gè)表示該任務(wù)的 Future。
<T> Future<T> submit(Runnable task, T result) 提交一個(gè) Runnable 任務(wù)用于執(zhí)行,并返回一個(gè)表示該任務(wù)的 Future。
3.2 execute是Executor接口的方法
他雖然也可以像submit那樣讓一個(gè)任務(wù)執(zhí)行 但并不能有返回值
voidexecute(Runnablecommand)
在未來某個(gè)時(shí)間執(zhí)行給定的命令。該命令可能在新的線程、已入池的線程或者正調(diào)用的線程中執(zhí)行,這由 Executor 實(shí)現(xiàn)決定。
(4 Future
Future 表示異步計(jì)算的結(jié)果。
它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并獲取計(jì)算的結(jié)果。
計(jì)算完成后只能使用 get 方法來獲取結(jié)果,如有必要,計(jì)算完成前可以阻塞此方法。
取消則由 cancel 方法來執(zhí)行。還提供了其他方法,以確定任務(wù)是正常完成還是被取消了。一旦計(jì)算完成,就不能再取消計(jì)算。
如果為了可取消性而使用 Future 但又不提供可用的結(jié)果,則可以聲明 Future<?> 形式類型、并返回 null 作為底層任務(wù)的結(jié)果。
Future就是對(duì)于具體的Runnable或者Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消、查詢是否完成、獲取結(jié)果。
必要時(shí)可以通過get方法獲取執(zhí)行結(jié)果,該方法會(huì)阻塞直到任務(wù)返回結(jié)果。
也就是說Future提供了三種功能:
--判斷任務(wù)是否完成;
--能夠中斷任務(wù);
--能夠獲取任務(wù)執(zhí)行結(jié)果。
boolean cancel(boolean mayInterruptIfRunning) 試圖取消對(duì)此任務(wù)的執(zhí)行。
V get() 如有必要,等待計(jì)算完成,然后獲取其結(jié)果。
V get(long timeout, TimeUnit unit) 如有必要,最多等待為使計(jì)算完成所給定的時(shí)間之后,獲取其結(jié)果(如果結(jié)果可用)。
boolean isCancelled() 如果在任務(wù)正常完成前將其取消,則返回 true。
boolean isDone() 如果任務(wù)已完成,則返回 true。
4線程池簡單使用案例2
java.util.concurrent.Executors類的API提供大量創(chuàng)建連接池的靜態(tài)方法:
1 import java.util.concurrent.Executors;
2 import java.util.concurrent.ExecutorService;
3 public class JavaThreadPool {
4 public static void main(String[] args) {
5 // 創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池
6 ExecutorService pool = Executors.newFixedThreadPool(2);
7 // 創(chuàng)建實(shí)現(xiàn)了Runnable接口對(duì)象,Thread對(duì)象當(dāng)然也實(shí)現(xiàn)了Runnable接口
8 Thread t1 = new MyThread();
9 Thread t2 = new MyThread();
10 Thread t3 = new MyThread();
11 Thread t4 = new MyThread();
12 Thread t5 = new MyThread();
13 // 將線程放入池中進(jìn)行執(zhí)行
14 pool.execute(t1);
15 pool.execute(t2);
16 pool.execute(t3);
17 pool.execute(t4);
18 pool.execute(t5);
19 // 關(guān)閉線程池
20 pool.shutdown();
21 }
22 }
23 class MyThread extends Thread {
24 @Override
25 public void run() {
26 System.out.println(Thread.currentThread().getName() + "正在執(zhí)行… …");
27 }
28 }
運(yùn)行效果示例:
pool-1-thread-2正在執(zhí)行… …
pool-1-thread-2正在執(zhí)行… …
pool-1-thread-2正在執(zhí)行… …
pool-1-thread-2正在執(zhí)行… …
pool-1-thread-1正在執(zhí)行… …
或
pool-1-thread-1正在執(zhí)行… …
pool-1-thread-1正在執(zhí)行… …
pool-1-thread-1正在執(zhí)行… …
pool-1-thread-1正在執(zhí)行… …
pool-1-thread-2正在執(zhí)行… …
可見線程池中有2個(gè)線程在工作,可見newFixedThreadPool 是固定大小的線程池
5 單任務(wù)線程池:
//創(chuàng)建一個(gè)使用單個(gè) worker 線程的 Executor,以無界隊(duì)列方式來運(yùn)行該線程。
ExecutorService pool = Executors.newSingleThreadExecutor();
案例:
1 import java.util.concurrent.ExecutorService;
2 import java.util.concurrent.Executors;
3
4 public class SingleThreadPollDemo {
5
6 public static void main(String[] args) {
7 // 創(chuàng)建一個(gè)使用單個(gè) worker 線程的 Executor,以無界隊(duì)列方式來運(yùn)行該線程。
8 ExecutorService pool = Executors.newSingleThreadExecutor();
9
10 Runnable task1 = new SingelTask();
11 Runnable task2 = new SingelTask();
12 Runnable task3 = new SingelTask();
13
14 pool.execute(task3);
15 pool.execute(task2);
16 pool.execute(task1);
17
18 // 等待已提交的任務(wù)全部結(jié)束 不再接受新的任務(wù)
19 pool.shutdown();
20 }
21 }
22
23 class SingelTask implements Runnable{
24
25 @Override
26 public void run() {
27 System.out.println(Thread.currentThread().getName() + "正在執(zhí)行… …");
28 try {
29 Thread.sleep(3000);
30 } catch (InterruptedException e) {
31 e.printStackTrace();
32 }
33 System.out.println(Thread.currentThread().getName() + "執(zhí)行完畢");
34 }
35
36 }
運(yùn)行結(jié)果:
pool-1-thread-1正在執(zhí)行… …
pool-1-thread-1執(zhí)行完畢
pool-1-thread-1正在執(zhí)行… …
pool-1-thread-1執(zhí)行完畢
pool-1-thread-1正在執(zhí)行… …
pool-1-thread-1執(zhí)行完畢
可見線程池中只有一個(gè)線程在執(zhí)行任務(wù)
6 小結(jié):
對(duì)于以上兩種連接池,大小都是固定的,當(dāng)要加入的池的線程(或者任務(wù))超過池最大尺寸時(shí)候,則入此線程池需要排隊(duì)等待。
一旦池中有線程完畢,則排隊(duì)等待的某個(gè)線程會(huì)入池執(zhí)行。
其他線程池示例:
固定大小線程池
importjava.util.concurrent.Executors;
importjava.util.concurrent.ExecutorService;
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(t1);
pool.shutdown();
單任務(wù)線程池
ExecutorService pool = Executors.newSingleThreadExecutor();
可變尺寸線程池
ExecutorService pool = Executors.newCachedThreadPool();
延遲連接池
importjava.util.concurrent.Executors;
importjava.util.concurrent.ScheduledExecutorService;
importjava.util.concurrent.TimeUnit;
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
pool.schedule(t4, 10, TimeUnit.MILLISECONDS);
單任務(wù)延遲連接池
ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();
7使用示例
1 public class Test {
2 public static void main(String[] args) {
3 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
4 new ArrayBlockingQueue<Runnable>(5));
5
6 for(int i=0;i<15;i++){
7 MyTask myTask = new MyTask(i);
8 executor.execute(myTask);
9 System.out.println("線程池中線程數(shù)目:"+executor.getPoolSize()+",隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:"+
10 executor.getQueue().size()+",已執(zhí)行完別的任務(wù)數(shù)目:"+executor.getCompletedTaskCount());
11 }
12 executor.shutdown();
13 }
14 }
15
16
17 class MyTask implements Runnable {
18 private int taskNum;
19
20 public MyTask(int num) {
21 this.taskNum = num;
22 }
23
24 @Override
25 public void run() {
26 System.out.println("正在執(zhí)行task "+taskNum);
27 try {
28 Thread.currentThread().sleep(4000);
29 } catch (InterruptedException e) {
30 e.printStackTrace();
31 }
32 System.out.println("task "+taskNum+"執(zhí)行完畢");
33 }
34 }
執(zhí)行結(jié)果:
正在執(zhí)行task 0 線程池中線程數(shù)目:1,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行玩別的任務(wù)數(shù)目:0 線程池中線程數(shù)目:2,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 1 線程池中線程數(shù)目:3,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 2 線程池中線程數(shù)目:4,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 3 線程池中線程數(shù)目:5,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:0,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 4 線程池中線程數(shù)目:5,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:1,已執(zhí)行玩別的任務(wù)數(shù)目:0 線程池中線程數(shù)目:5,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:2,已執(zhí)行玩別的任務(wù)數(shù)目:0 線程池中線程數(shù)目:5,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:3,已執(zhí)行玩別的任務(wù)數(shù)目:0 線程池中線程數(shù)目:5,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:4,已執(zhí)行玩別的任務(wù)數(shù)目:0 線程池中線程數(shù)目:5,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行玩別的任務(wù)數(shù)目:0 線程池中線程數(shù)目:6,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 10 線程池中線程數(shù)目:7,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 11 線程池中線程數(shù)目:8,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 12 線程池中線程數(shù)目:9,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 13 線程池中線程數(shù)目:10,隊(duì)列中等待執(zhí)行的任務(wù)數(shù)目:5,已執(zhí)行玩別的任務(wù)數(shù)目:0 正在執(zhí)行task 14 task 3執(zhí)行完畢 task 0執(zhí)行完畢 task 2執(zhí)行完畢 task 1執(zhí)行完畢 正在執(zhí)行task 8 正在執(zhí)行task 7 正在執(zhí)行task 6 正在執(zhí)行task 5 task 4執(zhí)行完畢 task 10執(zhí)行完畢 task 11執(zhí)行完畢 task 13執(zhí)行完畢 task 12執(zhí)行完畢 正在執(zhí)行task 9 task 14執(zhí)行完畢 task 8執(zhí)行完畢 task 5執(zhí)行完畢 task 7執(zhí)行完畢 task 6執(zhí)行完畢 task 9執(zhí)行完畢
從執(zhí)行結(jié)果可以看出,當(dāng)線程池中線程的數(shù)目大于5時(shí),便將任務(wù)放入任務(wù)緩存隊(duì)列里面,當(dāng)任務(wù)緩存隊(duì)列滿了之后,便創(chuàng)建新的線程。
如果上面程序中,將for循環(huán)中改成執(zhí)行20個(gè)任務(wù),就會(huì)拋出任務(wù)拒絕異常了。
不過在java doc中,并不提倡我們直接使用ThreadPoolExecutor,而是使用Executors類中提供的幾個(gè)靜態(tài)方法來創(chuàng)建線程池:
1 Executors.newCachedThreadPool(); //創(chuàng)建一個(gè)緩沖池,緩沖池容量大小為Integer.MAX_VALUE 2 Executors.newSingleThreadExecutor(); //創(chuàng)建容量為1的緩沖池 3 Executors.newFixedThreadPool(int); //創(chuàng)建固定容量大小的緩沖池
下面是這三個(gè)靜態(tài)方法的具體實(shí)現(xiàn):
1 public static ExecutorService newFixedThreadPool(int nThreads) {
2 return new ThreadPoolExecutor(nThreads, nThreads,
3 0L, TimeUnit.MILLISECONDS,
4 new LinkedBlockingQueue<Runnable>());
5 }
6 public static ExecutorService newSingleThreadExecutor() {
7 return new FinalizableDelegatedExecutorService
8 (new ThreadPoolExecutor(1, 1,
9 0L, TimeUnit.MILLISECONDS,
10 new LinkedBlockingQueue<Runnable>()));
11 }
12 public static ExecutorService newCachedThreadPool() {
13 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
14 60L, TimeUnit.SECONDS,
15 new SynchronousQueue<Runnable>());
16 }
從它們的具體實(shí)現(xiàn)來看,它們實(shí)際上也是調(diào)用了ThreadPoolExecutor,只不過參數(shù)都已配置好了。
newFixedThreadPool創(chuàng)建的線程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
newSingleThreadExecutor將corePoolSize和maximumPoolSize都設(shè)置為1,也使用的LinkedBlockingQueue;
newCachedThreadPool將corePoolSize設(shè)置為0,將maximumPoolSize設(shè)置為Integer.MAX_VALUE,使用的SynchronousQueue,也就是說來了任務(wù)就創(chuàng)建線程運(yùn)行,當(dāng)線程空閑超過60秒,就銷毀線程。
實(shí)際中,如果Executors提供的三個(gè)靜態(tài)方法能滿足要求,就盡量使用它提供的三個(gè)方法,因?yàn)樽约喝ナ謩?dòng)配置ThreadPoolExecutor的參數(shù)有點(diǎn)麻煩,要根據(jù)實(shí)際任務(wù)的類型和數(shù)量來進(jìn)行配置。
另外,如果ThreadPoolExecutor達(dá)不到要求,可以自己繼承ThreadPoolExecutor類進(jìn)行重寫。
總結(jié)
以上是生活随笔為你收集整理的java 线程池 使用实例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿波罗尼斯圆
- 下一篇: 组装台式机怎么装系统台式电脑如何组装