簡述
在面向軟件編程中涩金,創(chuàng)建和銷毀對象是一件非常耗時的事情筐带,因為創(chuàng)建一個對象要獲取內(nèi)存資源或者其它更多的資源泽论。在Java中更是如此桐罕,虛擬機將試圖跟蹤每一個對象默怨,以便能在對象銷毀時進行回收。所以提供程序效率的方法就是減少對象的創(chuàng)建和銷毀久锥。如何利用已有的對象來服務(wù)就是一個需要解決的問題家淤。
Java線程池實現(xiàn)了一個Java高并發(fā)的、Java多線程的瑟由、可管理的統(tǒng)一調(diào)度器絮重。java.util.concurrent.Executors工作中最常用的和熟知的,順便一提歹苦,java.util.concurrent包由出自著名的大神Doug Lea之手青伤。
Executors是個線程工廠類,方便快速地創(chuàng)建很多線程池殴瘦,也可以說是一個線程池工具類狠角。配置一個線程池是比較復(fù)雜的,尤其是對于線程池原理不是很清楚的情況下蚪腋,很可能配置的線程池不是最優(yōu)的丰歌,以至于達不到預(yù)期的效果,因此Executors類里面給我們提供了一些靜態(tài)工廠方法屉凯,以便我們能生成常用的線程池立帖。
常用的方法有如下三種:
- 1、newSingleThreadExecutor:創(chuàng)建一個單線程的線程池悠砚。
- 2晓勇、newFixedThreadPool:創(chuàng)建固定大小的線程池。
- 3、newCachedThreadPool:創(chuàng)建一個可緩存的線程池绑咱。
1绰筛、newSingleThreadExecutor的使用
創(chuàng)建一個單線程的線程池。這個線程只有一個線程在工作描融,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)别智。如果這個唯一的線程因為異常而結(jié)束,那么會有一個新的線程來代替它稼稿。此線程保證所有的任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行薄榛。
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService=Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int no=i;
Runnable task=new Runnable() {
@Override
public void run() {
try{
System.out.println("into "+no);
Thread.sleep(1000L);
System.out.println("end "+no);
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
//交由線程池處理任務(wù)
executorService.execute(task);
}
executorService.shutdown();
System.out.println("main thread have terminate");
}
}
運行結(jié)果如下所示:
從截圖中可以得知任務(wù)是一條一條的執(zhí)行的。
查看一下Executors.newSingleThreadExecutor()的實現(xiàn)方法:
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* <tt>newFixedThreadPool(1)</tt> the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
通過源碼得知让歼,它是調(diào)用了ThreadPoolExecutor創(chuàng)建了一個LinkedBlockingQueue的一個大小的線程池敞恋,采用默認(rèn)的異常策略。
2谋右、newCachedThreadPool的使用
創(chuàng)建一個緩沖池大小可根據(jù)需要伸縮的線程池硬猫,但是在以前構(gòu)造的線程可用時將重用它們。對于執(zhí)行很多短期異步任務(wù)而言改执,這些線程池通承ッ郏可提供程序性能。調(diào)用execute將重用以前構(gòu)造的線程(如果線程可用)辈挂。如果現(xiàn)有線程沒有可用的衬横,則創(chuàng)建一個新線程并添加到池中。終止并從緩存中移除那些已有60s未被使用的線程终蒂。因此蜂林,長時間保持空閑的線程池不會使用任何資源。
Demo:
public class CachedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService=Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
final int no=i;
Runnable task=new Runnable() {
@Override
public void run() {
try{
System.out.println("into "+no);
Thread.sleep(10001L);
System.out.println("end "+no);
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
executorService.execute(task);
}
System.out.println("main thread have terminate");
executorService.shutdown();
}
}
從運行結(jié)果可知拇泣,任務(wù)一開始所有的線程就開始執(zhí)行了噪叙,都在互相爭奪CPU資源。
接下來我們看下它的源碼實現(xiàn):
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to <tt>execute</tt> will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
代碼創(chuàng)建了一個內(nèi)核線程池霉翔。線程為零睁蕾,來一個線程就在線程池里面創(chuàng)建一個SynchronousQueue。
3债朵、newFixedThreadPool的使用
創(chuàng)建一個可重用固定線程數(shù)的線程池子眶,以共享的無界隊列方式來運行這些線程。在任意點葱弟,在大多數(shù)nThreads線程會處于處理任務(wù)的活動狀態(tài)壹店。如果在所有線程處于活動狀態(tài)時提交附加任務(wù),則在有可用線程之前芝加,附加任務(wù)將在隊列中等待。如果在關(guān)閉前的執(zhí)行期間由于失敗而導(dǎo)致任何線程終止,那么一個新的線程將代替它執(zhí)行后續(xù)任務(wù)(如果需要)藏杖。在某個線程被顯示關(guān)閉之前将塑,池中的線程將一直存在。
Demo:
public class newFixedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService=Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
final int no=i;
Runnable task=new Runnable() {
@Override
public void run() {
try{
System.out.println("into "+no);
Thread.sleep(1000L);
System.out.println("end "+no);
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
executorService.execute(task);
}
System.out.println("main thread have terminate");
executorService.shutdown();
}
}
運行結(jié)果如下:
從結(jié)果上面看蝌麸,一下子只有5個線程同時執(zhí)行点寥,然后結(jié)束一個再執(zhí)行一個。
接下來看下源碼實現(xiàn):
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* <tt>nThreads</tt> threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
代碼創(chuàng)建了一個指定大小的LinkedBlockingQueue的線程池来吩。
線程池的好處
1敢辩、合理利用線程能帶來4個好處
(1). 降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程弟疆,降低線程創(chuàng)建和銷毀造成的消耗戚长。
(2). 提供響應(yīng)速度。當(dāng)任務(wù)到達時怠苔,任務(wù)可以不需要等待線程創(chuàng)建就能立即執(zhí)行同廉。
(3).提供線程的可管理性。線程是稀缺資源柑司,如果無限制的創(chuàng)建迫肖,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性攒驰,使用線程池可以進行統(tǒng)一分配蟆湖、調(diào)優(yōu)和監(jiān)控。但是要做到合理地利用線程玻粪,必須對其原理了如指掌帐姻。
(4).防止服務(wù)器過載,形成內(nèi)存溢出奶段,或者CPU耗盡饥瓷。
2、線程池技術(shù)如何高服務(wù)器程序的性能
這里所提及的服務(wù)器程序是指能夠接收客戶端請求并處理請求的程序痹籍,而不只是那些接受網(wǎng)絡(luò)客戶端請求的網(wǎng)絡(luò)服務(wù)器程序呢铆。多線程技術(shù)主要解決處理器單元內(nèi)多個線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時間蹲缠,增加處理器單元的吞吐能力棺克。但如果對多線程應(yīng)用不當(dāng),會增加對單個任務(wù)的處理時間线定。
3娜谊、舉個栗子
假設(shè)在一臺服務(wù)器完成一項任務(wù)的時間為T,并假設(shè):
T1斤讥,創(chuàng)建線程的時間纱皆。
T2,在線程中執(zhí)行任務(wù)所花費的時間,包括線程同步所需時間
-
T3派草,線程銷毀時間
顯然T=T1+T2+T3搀缠。這是一個極度簡化的假設(shè)栗子。
可以看出T1近迁、T3是多線程本身帶來的開銷艺普,我們渴望減少T1、T3所用的時間從而減少T的時間鉴竭。但一些線程的使用者并沒有注意到這一點歧譬,所以在程序中頻繁的創(chuàng)建和銷毀線程,這導(dǎo)致T1和T3在T中占有相當(dāng)比例搏存。顯然這是突出了線程的弱點(T1瑰步、T3),而不是優(yōu)點(并發(fā)性)祭埂。線程池技術(shù)正是關(guān)注如何縮短調(diào)整T1面氓、T3時間的技術(shù),從而提高服務(wù)器程序性能的蛆橡。它把T1舌界、T3分別安排在服務(wù)器程序啟動或結(jié)束的時間段或者一些空閑的時間段,這樣在服務(wù)器程序處理客戶端請求時泰演,不會有T1呻拌、T3的開銷了。
線程池不僅調(diào)整了T1睦焕、T3產(chǎn)生的時間段藐握,而且它還顯著減少了創(chuàng)建線程的數(shù)目。再看一個栗子:
假設(shè)一個服務(wù)器一天要處理50000個請求垃喊,并且每個請求需要一個單獨的線程數(shù)目完成猾普。我們比較一下利用線程技術(shù)和不利用線程池技術(shù)的服務(wù)器處理這些請求時所產(chǎn)生的線程總數(shù)。在線程池中本谜,線程數(shù)一般是固定的初家,所以產(chǎn)生線程的總數(shù)不會超過線程池中的數(shù)目或者上限(以下簡稱線程池尺寸),而如果服務(wù)器不利用線程池來處理這些請求則線程總數(shù)為50000.一般線程池尺寸是遠(yuǎn)遠(yuǎn)小于50000的乌助。所以利用線程池的服務(wù)器程序不會為了創(chuàng)建50000而在處理請求時浪費時間溜在,從而提高整體效率。
4他托、線程池應(yīng)用范圍
(1)需要大量的線程來完成任務(wù)掖肋,且完成任務(wù)的時間比較短。Web服務(wù)器完成網(wǎng)頁請求這樣的任務(wù)赏参,使用多線程池技術(shù)是非常合適的志笼。因為單個任務(wù)小沿盅,而任務(wù)數(shù)量巨大,你可以想象一個熱門網(wǎng)站的點擊次數(shù)籽腕。但對于長時間的任務(wù)嗡呼,比如一個Telnet連接請求纸俭,線程池的優(yōu)點就不明顯了皇耗。因為Telnet回話時間比線程的創(chuàng)建時間打多了。
(2)對性能要求苛刻的應(yīng)用揍很,比如要求服務(wù)器迅速響應(yīng)客戶端請求郎楼。
(3)接收突發(fā)性的大量請求,但不至于使用服務(wù)器因此產(chǎn)生大量線程的應(yīng)用窒悔。突發(fā)性大量客戶請求呜袁,在沒有線程池的情況下,將產(chǎn)生大量的線程简珠,雖然理論上大部分操作系統(tǒng)線程數(shù)目最大值不是問題阶界,短時間內(nèi)產(chǎn)生大量線程可能使內(nèi)存到達極限,并出現(xiàn)OutOfMemory的錯誤聋庵。
The end: next: 線程池工作機制與原理