java面試之線程池相關(guān)

  • 379.什么是線程池?

  • 什么是線程池?
    線程池是指在初始化一個(gè)多線程應(yīng)用程序過(guò)程中創(chuàng)建一個(gè)線程集合,然后在需要執(zhí)行新的任務(wù)時(shí)重用這些線程而不是新建一個(gè)線程。線程池中線程的數(shù)量通常完全取決于可用內(nèi)存數(shù)量和應(yīng)用程序的需求。然而,增加可用線程數(shù)量是可能的。線程池中的每個(gè)線程都有被分配一個(gè)任務(wù),一旦任務(wù)已經(jīng)完成了,線程回到池子中并等待下一次分配任務(wù)。

  • 380.使用線程池有那些好處?

    • 改進(jìn)了一個(gè)應(yīng)用程序的響應(yīng)時(shí)間。由于線程池中的線程已經(jīng)準(zhǔn)備好且等待被分配任務(wù),應(yīng)用程序可以直接拿來(lái)使用而不用新建一個(gè)線程。

為什么需要線程池?
基于以下幾個(gè)原因在多線程應(yīng)用程序中使用線程是必須的:

  1. 線程池改進(jìn)了一個(gè)應(yīng)用程序的響應(yīng)時(shí)間。由于線程池中的線程已經(jīng)準(zhǔn)備好且等待被分配任務(wù),應(yīng)用程序可以直接拿來(lái)使用而不用新建一個(gè)線程。

  2. 線程池節(jié)省了CLR 為每個(gè)短生存周期任務(wù)創(chuàng)建一個(gè)完整的線程的開(kāi)銷并可以在任務(wù)完成后回收資源。

  3. 線程池根據(jù)當(dāng)前在系統(tǒng)中運(yùn)行的進(jìn)程來(lái)優(yōu)化線程時(shí)間片。

  4. 線程池允許我們開(kāi)啟多個(gè)任務(wù)而不用為每個(gè)線程設(shè)置屬性。

  5. 線程池允許我們?yōu)檎趫?zhí)行的任務(wù)的程序參數(shù)傳遞一個(gè)包含狀態(tài)信息的對(duì)象引用。

  6. 線程池可以用來(lái)解決處理一個(gè)特定請(qǐng)求最大線程數(shù)量限制問(wèn)題。

線程池的概念
影響一個(gè)多線程應(yīng)用程序的相應(yīng)時(shí)間的幾個(gè)主要因素之一是為每個(gè)任務(wù)生成一個(gè)線程的時(shí)間。

例如,一個(gè)Web Server 是一個(gè)多線程應(yīng)用程序,它可以同時(shí)對(duì)多個(gè)客戶端請(qǐng)求提供服務(wù)。讓我們假設(shè)有十個(gè)客戶端同時(shí)訪問(wèn)Web Server:

  1. 如果服務(wù)執(zhí)行一個(gè)客戶端對(duì)應(yīng)一個(gè)線程的策略,它將為這些客戶端生成十個(gè)新線程,從創(chuàng)建第一個(gè)線程開(kāi)始到在線程的整個(gè)生命周期管理它們都會(huì)增加系統(tǒng)開(kāi)銷。也有可能在某個(gè)時(shí)間計(jì)算機(jī)的資源耗盡。

  2. 相反的,如果服務(wù)端使用一個(gè)線程池來(lái)處理這些請(qǐng)求,那么當(dāng)每次客戶端請(qǐng)求來(lái)到后都創(chuàng)建一個(gè)線程的時(shí)間會(huì)節(jié)省下來(lái)。它可以管理已經(jīng)創(chuàng)建的線程,如果線程池太忙的話也可以拒絕客戶端請(qǐng)求。這是線程池背后的概念。

.NET CLR 為服務(wù)請(qǐng)求維護(hù)一個(gè)線程池。如果我們的應(yīng)用程序從線程池中請(qǐng)求一個(gè)新線程,CLR 將試著從線程池中取出一個(gè)。如果線程池是空的,它將生成一個(gè)新線程并把它給我們。當(dāng)我們的代碼使用的線程結(jié)束以后,線程由.NET 回收并返回給線程池。線程池中線程的數(shù)量由當(dāng)前可用地內(nèi)存數(shù)量決定。

現(xiàn)在回顧一下,影響設(shè)計(jì)一個(gè)多線程應(yīng)用程序的因素有:

  1. 一個(gè)應(yīng)用程序的響應(yīng)時(shí)間。

  2. 線程管理資源的分配。

  3. 資源共享。

  4. 線程同步。

第1種方式:配置Connector
maxThreads:tomcat可用于請(qǐng)求處理的最大線程數(shù)
minSpareThreads:tomcat初始線程數(shù),即最小空閑線程數(shù)
maxSpareThreads:tomcat最大空閑線程數(shù),超過(guò)的會(huì)被關(guān)閉
acceptCount:當(dāng)所有可以使用的處理請(qǐng)求的線程數(shù)都被使用時(shí),可以放到處理隊(duì)列中的請(qǐng)求數(shù),超過(guò)這個(gè)數(shù)的請(qǐng)求將不予處理

<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:超過(guò)最小空閑線程數(shù)時(shí),多的線程會(huì)等待這個(gè)時(shí)間長(zhǎng)度,然后關(guān)閉
threadPriority:線程優(yōu)先級(jí)

<Executor name="tomcatThreadPool" namePrefix="req-exec-" maxThreads="1000" minSpareThreads="50" maxIdleTime="60000"/>  
  
<Connector port="8080" protocol="HTTP/1.1" executor="tomcatThreadPool"/>  

-如何創(chuàng)建線程池?
Java通過(guò)Executors提供四種線程池,分別為:
newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程。
newFixedThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。
newScheduledThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。

(1) newCachedThreadPool
創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程。示例代碼如下:
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);
}
});
}
}
}

線程池為無(wú)限大,當(dāng)執(zhí)行第二個(gè)任務(wù)時(shí)第一個(gè)任務(wù)已經(jīng)完成,會(huì)復(fù)用執(zhí)行第一個(gè)任務(wù)的線程,而不用每次新建線程。

(2) newFixedThreadPool
創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。示例代碼如下:
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();
}
}
});
}
}
}

因?yàn)榫€程池大小為3,每個(gè)任務(wù)輸出index后sleep 2秒,所以每?jī)擅氪蛴?個(gè)數(shù)字。
定長(zhǎng)線程池的大小最好根據(jù)系統(tǒng)資源進(jìn)行設(shè)置。如Runtime.getRuntime().availableProcessors()

(3) newScheduledThreadPool
創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(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)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(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é)果依次輸出,相當(dāng)于順序執(zhí)行各個(gè)任務(wù)。
你可以使用JDK自帶的監(jiān)控工具來(lái)監(jiān)控我們創(chuàng)建的線程數(shù)量,運(yùn)行一個(gè)不終止的線程,創(chuàng)建指定量的線程,來(lái)觀察:
工具目錄:C:\Program Files\Java\jdk1.6.0_06\bin\jconsole.exe
運(yùn)行程序做稍微修改:
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();
}
}
}
}

效果如下:

選擇我們運(yùn)行的程序:

監(jiān)控運(yùn)行狀態(tài)

這里寫圖片描述
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容