379.什么是線程池?
什么是線程池贤徒?
線程池是指在初始化一個多線程應用程序過程中創(chuàng)建一個線程集合芹壕,然后在需要執(zhí)行新的任務(wù)時重用這些線程而不是新建一個線程。線程池中線程的數(shù)量通常完全取決于可用內(nèi)存數(shù)量和應用程序的需求接奈。然而踢涌,增加可用線程數(shù)量是可能的。線程池中的每個線程都有被分配一個任務(wù)序宦,一旦任務(wù)已經(jīng)完成了睁壁,線程回到池子中并等待下一次分配任務(wù)。-
380.使用線程池有那些好處互捌?
- 改進了一個應用程序的響應時間潘明。由于線程池中的線程已經(jīng)準備好且等待被分配任務(wù),應用程序可以直接拿來使用而不用新建一個線程秕噪。
為什么需要線程池钳降?
基于以下幾個原因在多線程應用程序中使用線程是必須的:
線程池改進了一個應用程序的響應時間。由于線程池中的線程已經(jīng)準備好且等待被分配任務(wù)腌巾,應用程序可以直接拿來使用而不用新建一個線程遂填。
線程池節(jié)省了CLR 為每個短生存周期任務(wù)創(chuàng)建一個完整的線程的開銷并可以在任務(wù)完成后回收資源。
線程池根據(jù)當前在系統(tǒng)中運行的進程來優(yōu)化線程時間片澈蝙。
線程池允許我們開啟多個任務(wù)而不用為每個線程設(shè)置屬性吓坚。
線程池允許我們?yōu)檎趫?zhí)行的任務(wù)的程序參數(shù)傳遞一個包含狀態(tài)信息的對象引用。
線程池可以用來解決處理一個特定請求最大線程數(shù)量限制問題灯荧。
線程池的概念
影響一個多線程應用程序的相應時間的幾個主要因素之一是為每個任務(wù)生成一個線程的時間礁击。
例如,一個Web Server 是一個多線程應用程序,它可以同時對多個客戶端請求提供服務(wù)客税。讓我們假設(shè)有十個客戶端同時訪問Web Server:
如果服務(wù)執(zhí)行一個客戶端對應一個線程的策略况褪,它將為這些客戶端生成十個新線程,從創(chuàng)建第一個線程開始到在線程的整個生命周期管理它們都會增加系統(tǒng)開銷更耻。也有可能在某個時間計算機的資源耗盡测垛。
相反的,如果服務(wù)端使用一個線程池來處理這些請求秧均,那么當每次客戶端請求來到后都創(chuàng)建一個線程的時間會節(jié)省下來食侮。它可以管理已經(jīng)創(chuàng)建的線程,如果線程池太忙的話也可以拒絕客戶端請求目胡。這是線程池背后的概念锯七。
.NET CLR 為服務(wù)請求維護一個線程池。如果我們的應用程序從線程池中請求一個新線程誉己,CLR 將試著從線程池中取出一個眉尸。如果線程池是空的,它將生成一個新線程并把它給我們巨双。當我們的代碼使用的線程結(jié)束以后噪猾,線程由.NET 回收并返回給線程池。線程池中線程的數(shù)量由當前可用地內(nèi)存數(shù)量決定筑累。
現(xiàn)在回顧一下袱蜡,影響設(shè)計一個多線程應用程序的因素有:
一個應用程序的響應時間。
線程管理資源的分配慢宗。
資源共享坪蚁。
線程同步。
第1種方式:配置Connector
maxThreads:tomcat可用于請求處理的最大線程數(shù)
minSpareThreads:tomcat初始線程數(shù)镜沽,即最小空閑線程數(shù)
maxSpareThreads:tomcat最大空閑線程數(shù)敏晤,超過的會被關(guān)閉
acceptCount:當所有可以使用的處理請求的線程數(shù)都被使用時,可以放到處理隊列中的請求數(shù)淘邻,超過這個數(shù)的請求將不予處理
<Connector port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" />
第2種方式:配置Executor和Connector
name:線程池的名字
class:線程池的類名
namePrefix:線程池中線程的命名前綴
maxThreads:線程池的最大線程數(shù)
minSpareThreads:線程池的最小空閑線程數(shù)
maxIdleTime:超過最小空閑線程數(shù)時茵典,多的線程會等待這個時間長度,然后關(guān)閉
threadPriority:線程優(yōu)先級
<Executor name="tomcatThreadPool" namePrefix="req-exec-" maxThreads="1000" minSpareThreads="50" maxIdleTime="60000"/>
<Connector port="8080" protocol="HTTP/1.1" executor="tomcatThreadPool"/>
-如何創(chuàng)建線程池宾舅?
Java通過Executors提供四種線程池统阿,分別為:
newCachedThreadPool創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要筹我,可靈活回收空閑線程扶平,若無可回收,則新建線程蔬蕊。
newFixedThreadPool 創(chuàng)建一個定長線程池结澄,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。
newScheduledThreadPool 創(chuàng)建一個定長線程池麻献,支持定時及周期性任務(wù)執(zhí)行们妥。
newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù)勉吻,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行监婶。
(1) newCachedThreadPool
創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要齿桃,可靈活回收空閑線程惑惶,若無可回收,則新建線程短纵。示例代碼如下:
Java代碼 收藏代碼
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
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();
}
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
}
線程池為無限大带污,當執(zhí)行第二個任務(wù)時第一個任務(wù)已經(jīng)完成,會復用執(zhí)行第一個任務(wù)的線程香到,而不用每次新建線程鱼冀。
(2) newFixedThreadPool
創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù)悠就,超出的線程會在隊列中等待雷绢。示例代碼如下:
Java代碼 收藏代碼
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
因為線程池大小為3,每個任務(wù)輸出index后sleep 2秒理卑,所以每兩秒打印3個數(shù)字。
定長線程池的大小最好根據(jù)系統(tǒng)資源進行設(shè)置蔽氨。如Runtime.getRuntime().availableProcessors()
(3) newScheduledThreadPool
創(chuàng)建一個定長線程池藐唠,支持定時及周期性任務(wù)執(zhí)行。延遲執(zhí)行示例代碼如下:
Java代碼 收藏代碼
package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
}
表示延遲3秒執(zhí)行鹉究。
定期執(zhí)行示例代碼如下:
Java代碼 收藏代碼
package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
}
}
表示延遲1秒后每3秒執(zhí)行一次宇立。
(4) newSingleThreadExecutor
創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù)自赔,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行妈嘹。示例代碼如下:
Java代碼 收藏代碼
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
結(jié)果依次輸出,相當于順序執(zhí)行各個任務(wù)绍妨。
你可以使用JDK自帶的監(jiān)控工具來監(jiān)控我們創(chuàng)建的線程數(shù)量润脸,運行一個不終止的線程,創(chuàng)建指定量的線程他去,來觀察:
工具目錄:C:\Program Files\Java\jdk1.6.0_06\bin\jconsole.exe
運行程序做稍微修改:
Java代碼 收藏代碼
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
while(true) {
System.out.println(index);
Thread.sleep(10 * 1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
效果如下:
選擇我們運行的程序:
監(jiān)控運行狀態(tài)