更新時(shí)間:2022-04-11 11:01:11 來源:動(dòng)力節(jié)點(diǎn) 瀏覽7684次
動(dòng)力節(jié)點(diǎn)小編來為大家講解,Java線程池重用先前創(chuàng)建的線程來執(zhí)行當(dāng)前任務(wù),并為線程周期開銷和資源抖動(dòng)問題提供了解決方案。由于請求到達(dá)時(shí)線程已經(jīng)存在,因此消除了線程創(chuàng)建引入的延遲,使應(yīng)用程序更具響應(yīng)性。
Java 提供了以 Executor 接口為中心的 Executor 框架,它的子接口ExecutorService和實(shí)現(xiàn)這兩個(gè)接口的類ThreadPoolExecutor 。通過使用執(zhí)行器,只需實(shí)現(xiàn) Runnable 對象并將它們發(fā)送到執(zhí)行器執(zhí)行。
它們允許您利用線程,但專注于您希望線程執(zhí)行的任務(wù),而不是線程機(jī)制。
要使用線程池,我們首先創(chuàng)建一個(gè) ExecutorService 對象并將一組任務(wù)傳遞給它。ThreadPoolExecutor 類允許設(shè)置核心和最大池大小。由特定線程運(yùn)行的可運(yùn)行對象按順序執(zhí)行。

Method Description
newFixedThreadPool(int) Creates a fixed size thread pool.
newCachedThreadPool() Creates a thread pool that creates new
threads as needed, but will reuse previously
constructed threads when they are available
newSingleThreadExecutor() Creates a single thread.
在固定線程池的情況下,如果執(zhí)行器當(dāng)前正在運(yùn)行所有線程,則將掛起的任務(wù)放入隊(duì)列中,并在線程空閑時(shí)執(zhí)行。
在下面的教程中,我們將看一個(gè)線程池執(zhí)行器的基本示例——FixedThreadPool。
應(yīng)遵循的步驟
1.創(chuàng)建一個(gè)任務(wù)(Runnable Object)來執(zhí)行
2.使用Executors創(chuàng)建Executor Pool
3.將任務(wù)傳遞給Executor Pool
4.關(guān)閉Executor Pool
// Java program to illustrate
// ThreadPool
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// Task class to be executed (Step 1)
class Task implements Runnable
{
private String name;
public Task(String s)
{
name = s;
}
// Prints task name and sleeps for 1s
// This Whole process is repeated 5 times
public void run()
{
try
{
for (int i = 0; i<=5; i++)
{
if (i==0)
{
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("Initialization Time for"
+ " task name - "+ name +" = " +ft.format(d));
//prints the initialization time for every task
}
else
{
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("Executing Time for task name - "+
name +" = " +ft.format(d));
// prints the execution time for every task
}
Thread.sleep(1000);
}
System.out.println(name+" complete");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public class Test
{
// Maximum number of threads in thread pool
static final int MAX_T = 3;
public static void main(String[] args)
{
// creates five tasks
Runnable r1 = new Task("task 1");
Runnable r2 = new Task("task 2");
Runnable r3 = new Task("task 3");
Runnable r4 = new Task("task 4");
Runnable r5 = new Task("task 5");
// creates a thread pool with MAX_T no. of
// threads as the fixed pool size(Step 2)
ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
// passes the Task objects to the pool to execute (Step 3)
pool.execute(r1);
pool.execute(r2);
pool.execute(r3);
pool.execute(r4);
pool.execute(r5);
// pool shutdown ( Step 4)
pool.shutdown();
}
}
樣品執(zhí)行
輸出:
任務(wù)名稱的初始化時(shí)間 - 任務(wù) 2 = 02:32:56
任務(wù)名稱的初始化時(shí)間 - 任務(wù) 1 = 02:32:56
任務(wù)名稱的初始化時(shí)間 - 任務(wù) 3 = 02:32:56
任務(wù)名稱的執(zhí)行時(shí)間 -任務(wù) 1 = 02:32:57
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 2 = 02:32:57
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 3 = 02:32:57
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 1 = 02:32:58
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 2 = 02:32:58
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 3 = 02:32:58
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 1 = 02:32:59
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 2 = 02:32:59
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 3 = 02:32:59
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 1 = 02:33:00
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 3 = 02:33:00
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 2 = 02:33:00
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 2 = 02:33:01
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 1 = 02:33:01
任務(wù)名稱 - 任務(wù) 3 的執(zhí)行時(shí)間 = 02:33:01
任務(wù) 2 完成
任務(wù) 1 完成
任務(wù) 3 完成
任務(wù)名稱 - 任務(wù) 5 的
初始化時(shí)間 = 02:33:02 任務(wù)名稱 - 任務(wù)的初始化時(shí)間4 = 02:33:02
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 4 = 02:33:03
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 5 = 02:33:03
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 5 = 02:33:04 正在
執(zhí)行任務(wù)名稱的時(shí)間 - 任務(wù) 4 = 02:33:04
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 4 = 02:33:05
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 5 = 02:33:05
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 5 = 02:33:06
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 4 = 02:33:06
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 5 = 02:33:07
任務(wù)名稱的執(zhí)行時(shí)間 - 任務(wù) 4 = 02:33:07
任務(wù) 5 完成
任務(wù) 4 完成
從程序的執(zhí)行中可以看出,只有當(dāng)池中的線程空閑時(shí),才會(huì)執(zhí)行任務(wù) 4 或任務(wù) 5。在那之前,額外的任務(wù)被放置在一個(gè)隊(duì)列中。


死鎖:雖然死鎖可能發(fā)生在任何多線程程序中,但線程池引入了另一種死鎖情況,在這種情況下,由于線程不可用,所有正在執(zhí)行的線程都在等待隊(duì)列中等待的阻塞線程的結(jié)果。
線程泄漏:如果線程從池中刪除以執(zhí)行任務(wù)但在任務(wù)完成時(shí)沒有返回給它,則會(huì)發(fā)生線程泄漏。例如,如果線程拋出異常并且池類沒有捕捉到這個(gè)異常,那么線程將簡單地退出,將線程池的大小減少一。如果這種情況重復(fù)很多次,那么池最終會(huì)變空,并且沒有線程可用于執(zhí)行其他請求。
資源抖動(dòng):如果線程池大小非常大,那么在線程之間的上下文切換中浪費(fèi)時(shí)間。正如解釋的那樣,擁有比最佳數(shù)量更多的線程可能會(huì)導(dǎo)致導(dǎo)致資源抖動(dòng)的饑餓問題。
要點(diǎn)
不要將同時(shí)等待其他任務(wù)結(jié)果的任務(wù)排隊(duì)。這可能導(dǎo)致如上所述的死鎖情況。
使用線程進(jìn)行長期操作時(shí)要小心。這可能會(huì)導(dǎo)致線程永遠(yuǎn)等待并最終導(dǎo)致資源泄漏。
線程池必須在最后顯式結(jié)束。如果不這樣做,那么程序?qū)⒗^續(xù)執(zhí)行并且永遠(yuǎn)不會(huì)結(jié)束。在池上調(diào)用 shutdown() 以結(jié)束執(zhí)行程序。如果您在關(guān)閉后嘗試向執(zhí)行器發(fā)送另一個(gè)任務(wù),它將拋出 RejectedExecutionException。
需要了解有效調(diào)整線程池的任務(wù)。如果任務(wù)差異很大,那么為不同類型的任務(wù)使用不同的線程池以便正確調(diào)整它們是有意義的。
您可以限制可以在 JVM 中運(yùn)行的最大線程數(shù),從而減少 JVM 內(nèi)存不足的機(jī)會(huì)。
如果您需要實(shí)現(xiàn)循環(huán)來創(chuàng)建新線程進(jìn)行處理,使用 ThreadPool 將有助于更快地處理,因?yàn)?ThreadPool 在達(dá)到最大限制后不會(huì)創(chuàng)建線程。
Thread Processing 完成后,ThreadPool 可以使用同一個(gè) Thread 做另一個(gè)進(jìn)程(這樣可以節(jié)省創(chuàng)建另一個(gè) Thread 的時(shí)間和資源。)
線程池的最佳大小取決于可用處理器的數(shù)量和任務(wù)的性質(zhì)。在一個(gè)只有計(jì)算類型進(jìn)程的隊(duì)列的 N 處理器系統(tǒng)上,最大線程池大小為 N 或 N+1 將實(shí)現(xiàn)最大效率。但是任務(wù)可能會(huì)等待 I/O,在這種情況下,我們會(huì)考慮比率請求的等待時(shí)間(W)和服務(wù)時(shí)間(S);導(dǎo)致最大池大小為 N*(1+ W/S) 以獲得最大效率。
線程池是組織服務(wù)器應(yīng)用程序的有用工具。它在概念上非常簡單,但是在實(shí)現(xiàn)和使用它時(shí)需要注意幾個(gè)問題,例如死鎖、資源抖動(dòng)。使用執(zhí)行器服務(wù)更容易實(shí)現(xiàn)。
相關(guān)閱讀

初級 202925

初級 203221

初級 202629

初級 203743