在JAVA中確定線性池大小,分別介紹CPU密集型任務和I/O密集型任務及其處理方法
在Java中,線程創(chuàng)建會產(chǎn)生顯著的開銷。創(chuàng)建線程會消耗時間,增加請求處理的延遲,并且涉及JVM和操作系統(tǒng)的相當多工作。為減輕這些開銷,線程池應運而生。
線程池(ThreadPool)是由執(zhí)行器服務(executor service)管理的工作線程池。其理念是重用現(xiàn)有線程,而不是為每個任務創(chuàng)建新線程。這可以通過減少線程創(chuàng)建的開銷來顯著提高應用程序的性能。Java的ExecutorService和ThreadPoolExecutor類提供了管理線程池的框架。
關鍵點
線程重用:線程池的線程可用于多個任務的重用。
任務排隊:任務被提交到池中,池中的線程會提取并執(zhí)行這些任務。
資源管理:可配置線程池大小、任務隊列大小和其他參數(shù),以高效管理資源。
1. 使用線性池的原因
性能:線程的創(chuàng)建和銷毀成本較高,尤其在Java中。創(chuàng)建一個可供多任務重用的線程池可減少這種開銷。
可擴展性:線程池可根據(jù)應用程序的需要進行擴展。例如,在負載較重時,可擴展線程池以處理額外的任務。
資源管理:線程池可幫助管理線程使用資源。例如,線程池可限制同時活動的線程數(shù)量,防止應用程序內(nèi)存不足。
2. 確定線程池大小:理解系統(tǒng)和資源限制
了解包括硬件和外部依賴關系的系統(tǒng)限制,對于確定線程池大小至關重要。下面通過一個例子來詳細說明這一概念。
假設在開發(fā)一個處理傳入HTTP請求的Web應用程序,每個請求可能涉及數(shù)據(jù)庫處理數(shù)據(jù)和調用外部第三方服務。目標是確定處理這些請求的最佳線程池大小。
此情況下需考慮因素包含數(shù)據(jù)庫連接池與外部服務吞吐量兩方面。
數(shù)據(jù)庫連接池:假設使用HikariCP之類的連接池來管理數(shù)據(jù)庫連接。您已將其配置為允許最多100個連接。如果您創(chuàng)建的線程超過可用連接數(shù),這些額外的線程將會等待可用連接,導致資源爭用和潛在的性能問題。
以下是配置HikariCP數(shù)據(jù)庫連接池的示例。
importcom.zaxxer.hikari.HikariConfig; importcom.zaxxer.hikari.HikariDataSource; publicclassDatabaseConnectionExample{ publicstaticvoidmain(String[]args){ HikariConfigconfig=newHikariConfig(); config.setJdbcUrl("jdbc//localhost:3306/mydb"); config.setUsername("username"); config.setPassword("password"); config.setMaximumPoolSize(100);//設置最大連接數(shù) HikariDataSourcedataSource=newHikariDataSource(config); //使用 dataSource 獲取數(shù)據(jù)庫連接并執(zhí)行查詢。 } }
外部服務吞吐量:此應用程序交互的外部服務有一個限制。它一次只能處理少量請求,例如10個請求。并發(fā)更多請求可能會使服務不堪重負,導致性能下降或出現(xiàn)錯誤。CPU核心確定服務器上可用的CPU核心數(shù)量對于優(yōu)化線程池大小至關重要。
intnumOfCores=Runtime.getRuntime().availableProcessors();
每個核心可以同時執(zhí)行一個線程。超出CPU核心數(shù)量的線程會導致過度的上下文切換,從而降低性能。
3.CPU密集型任務與I/O密集型任務
CPU密集型任務
CPU密集型任務是那些需要大量處理能力的任務,例如執(zhí)行復雜計算或運行模擬。這些任務通常受限于CPU的速度,而不是I/O設備的速度,如下列任務。
編譯和鏈接軟件
運行復雜模擬
執(zhí)行機器學習或數(shù)據(jù)挖掘任務
玩視頻游戲
要對CPU密集型任務進行優(yōu)化,應考慮多線程和并行性。并行處理是一種技術,用于將較大的任務劃分為較小的子任務,并將這些子任務分配到多個CPU核心或處理器上,以利用并發(fā)執(zhí)行并提高整體性能。假設有一個大型數(shù)字數(shù)組,要使用多個線程并發(fā)計算每個數(shù)字的平方,以利用并行處理,示例代碼如下。
importjava.util.concurrent.ExecutorService; importjava.util.concurrent.Executors; importjava.util.concurrent.TimeUnit; publicclassParallelSquareCalculator{ publicstaticvoidmain(String[]args){ int[]numbers={1,2,3,4,5,6,7,8,9,10}; intnumThreads=Runtime.getRuntime().availableProcessors();//獲取CPU核心數(shù)量 ExecutorServiceexecutorService=Executors.newFixedThreadPool(numThreads); for(intnumber:numbers){ executorService.submit(()->{ intsquare=calculateSquare(number); System.out.println("Squareof"+number+"is"+square); }); } executorService.shutdown(); try{ executorService.awaitTermination(Long.MAX_VALUE,TimeUnit.NANOSECONDS); }catch(InterruptedExceptione){ Thread.currentThread().interrupt(); } } privatestaticintcalculateSquare(intnumber){ //模擬耗時計算(例如,數(shù)據(jù)庫查詢、復雜計算) try{ Thread.sleep(1000);//模擬1秒延遲 }catch(InterruptedExceptione){ Thread.currentThread().interrupt(); } returnnumber*number; } }
I/O密集型任務
I/O密集型任務是那些與存儲設備(例如,讀寫文件)、網(wǎng)絡套接字(例如API調用)或用戶輸入進行交互(例如圖形用戶界面的交互)的任務,下面是一些典型的I/O密集型任務。
讀取或寫入大文件到磁盤(例如,保存視頻文件、加載數(shù)據(jù)庫)
通過網(wǎng)絡下載或上傳文件(例如,瀏覽網(wǎng)頁、觀看流媒體視頻)
發(fā)送和接收電子郵件
運行Web服務器或其他網(wǎng)絡服務
執(zhí)行數(shù)據(jù)庫查詢
Web服務器處理傳入請求
優(yōu)化I/O密集型任務的方式
在內(nèi)存中緩存頻繁訪問的數(shù)據(jù),以減少重復I/O操作的需要。
負載均衡,將I/O密集型任務分配到多個線程或進程中,以高效處理并發(fā)I/O操作。
使用SSD,固態(tài)硬盤(SSDs)相較于傳統(tǒng)硬盤(HDDs)可以顯著加快I/O操作。
使用高效數(shù)據(jù)結構,如哈希表和B樹,以減少所需的I/O操作次數(shù)。
避免不必要的文件操作,例如多次打開和關閉文件。
4.在兩種任務中確定線程數(shù)
確定CPU密集型任務的線程數(shù)量
對于CPU密集型任務,要最大化CPU利用率,而不讓系統(tǒng)因線程過多而超負荷,防止過度的上下文切換。一個常見方法是使用可用CPU核心數(shù)量。假設需開發(fā)一個視頻處理應用程序。視頻編碼是一項CPU密集型任務,需要應用復雜算法來壓縮視頻文件。有一個多核CPU可用。
計算可用CPU核心:使用Runtime.getRuntime().availableProcessors()確定可用的CPU核心數(shù)量。假設有8個核心。
創(chuàng)建線程池:創(chuàng)建一個大小接近或略少于可用CPU核心數(shù)量的線程池。在這種情況下,您可以選擇6或7個線程,以便為其他任務和系統(tǒng)進程留出一些CPU容量。
importjava.util.concurrent.ExecutorService; importjava.util.concurrent.Executors; publicclassVideoEncodingApp{ publicstaticvoidmain(String[]args){ intavailableCores=Runtime.getRuntime().availableProcessors(); intnumberOfThreads=Math.max(availableCores-1,1);//根據(jù)需要進行調整 ExecutorServicethreadPool=Executors.newFixedThreadPool(numberOfThreads); //將視頻編碼任務提交到線程池。 for(inti=0;i10;?i++)?{ ????????????threadPool.execute(()?->{ encodeVideo();//模擬視頻編碼任務 }); } threadPool.shutdown(); } privatestaticvoidencodeVideo(){ //模擬視頻編碼(CPU 密集型)任務。 //這里進行復雜計算和壓縮算法。 } }
確定I/O密集型任務的線程數(shù)量
對于I/O密集型任務,最佳線程數(shù)通常由I/O操作的性質和預期延遲決定。您希望擁有足夠的線程以保持I/O設備繁忙而不使其過載。理想的數(shù)量不必等于CPU核心的數(shù)量??紤]構建一個網(wǎng)頁爬蟲,下載網(wǎng)頁并提取信息。這涉及HTTP請求,這是因網(wǎng)絡延遲引起的I/O密集型任務,可從如下兩方面進行分析。
分析I/O延遲:估計預期的I/O延遲,這依賴于網(wǎng)絡或存儲。例如,如果每個 HTTP 請求大約需要500毫秒完成,您可能需要考慮I/O操作中的一些重疊。
創(chuàng)建線程池:創(chuàng)建一個大小平衡并行性與預期I/O延遲的線程池。您不一定需要為每個任務分配一個線程;相反,可以有一個較小的池,能夠高效管理I/O密集型任務。
以下是網(wǎng)頁爬蟲的示例代碼。
importjava.util.concurrent.ExecutorService; importjava.util.concurrent.Executors; publicclassWebPageCrawler{ publicstaticvoidmain(String[]args){ intexpectedIOLatency=500;//估計的I/O延遲(毫秒) intnumberOfThreads=4;//根據(jù)預期延遲和系統(tǒng)能力進行調整 ExecutorServicethreadPool=Executors.newFixedThreadPool(numberOfThreads); //要抓取的 URL 列表。 String[]urlsToCrawl={ "https://example.com", "https://google.com", "https://github.com", //在這里添加更多URL }; for(Stringurl:urlsToCrawl){ threadPool.execute(()->{ crawlWebPage(url,expectedIOLatency); }); } threadPool.shutdown(); } privatestaticvoidcrawlWebPage(Stringurl,intexpectedIOLatency){ //模擬網(wǎng)頁抓?。↖/O 密集型)任務。 //執(zhí)行 HTTP 請求并處理頁面內(nèi)容。 try{ Thread.sleep(expectedIOLatency);//模擬I/O延遲 }catch(InterruptedExceptione){ Thread.currentThread().interrupt(); } } }
5.總結公式
確定線程池大小的公式可以寫成如下形式。
線程數(shù)=可用核心數(shù)*目標CPU利用率*(1+等待時間/服務時間)
該公式各部分的詳細解釋如下。
可用核心數(shù):這是您的應用程序可用的 CPU 核心數(shù)量。重要的是要注意,這與 CPU 的數(shù)量不同,因為每個 CPU 可能有多個核心。
目標CPU利用率:這是您希望應用程序使用的 CPU 時間的百分比。如果將目標CPU利用率設置得過高,應用程序可能會變得無響應;如果設置得太低,應用程序將無法充分利用可用CPU資源。
等待時間:這是線程等待I/O操作完成的時間。這可能包括等待網(wǎng)絡響應、數(shù)據(jù)庫查詢或文件操作的時間。
服務時間:這是線程執(zhí)行計算的時間。
阻塞系數(shù):這是等待時間與服務時間的比率。它是衡量線程在 I/O 操作完成之前等待的時間相對于執(zhí)行計算時間的比例。
示例使用
假設有一個具有4個CPU核心的服務器,并且希望應用程序使用50%的可用 CPU資源。
您的應用程序有兩個任務類別:I/O密集型任務和CPU密集型任務。
I/O密集型任務的阻塞系數(shù)為0.5,意味著它們花費50%的時間等待I/O 操作完成。
線程數(shù)=4核心*0.5*(1+0.5)=3線程
CPU 密集型任務的阻塞系數(shù)為 0.1,意味著它們花費 10% 的時間等待 I/O 操作完成。
線程數(shù)=4核心*0.5*(1+0.1)=2.2線程
此示例創(chuàng)建了兩個線程池,一個用于I/O密集型任務,一個用于CPU密集型任務。I/O密集型線程池將有3個線程,CPU密集型線程池將有2個線程。
來源: 本文轉載自Java學研大本營公眾號
-
cpu
+關注
關注
68文章
11080瀏覽量
217110 -
操作系統(tǒng)
+關注
關注
37文章
7152瀏覽量
125596 -
JAVA
+關注
關注
20文章
2989瀏覽量
109747 -
線程池
+關注
關注
0文章
57瀏覽量
7138
原文標題:如何正確判斷Java線程池的大小
文章出處:【微信號:FPGA研究院,微信公眾號:FPGA研究院】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
Java中的線程池包括哪些

USB八口MODEM池 USB八口MODEM池 USB八口短信MODEM池 Q2406A
USB八口MODEM池 USB八口MODEM池
如何確定SPI Flash中FPGA配置的大小
內(nèi)存池可以調節(jié)內(nèi)存的大小嗎
關于RT-Thread內(nèi)存管理的內(nèi)存池簡析
java 常量池靜態(tài)變量詳解

數(shù)據(jù)庫連接池的設置怎么確定大小

評論