自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何正確判斷Java線程池的大小

開(kāi)發(fā) 前端
線程池(ThreadPool)是由執(zhí)行器服務(wù)(executor service)管理的工作線程池。其理念是重用現(xiàn)有線程,而不是為每個(gè)任務(wù)創(chuàng)建新線程。

在Java中,線程創(chuàng)建會(huì)產(chǎn)生顯著的開(kāi)銷。創(chuàng)建線程會(huì)消耗時(shí)間,增加請(qǐng)求處理的延遲,并且涉及JVM和操作系統(tǒng)的相當(dāng)多工作。為減輕這些開(kāi)銷,線程池應(yīng)運(yùn)而生。

線程池(ThreadPool)是由執(zhí)行器服務(wù)(executor service)管理的工作線程池。其理念是重用現(xiàn)有線程,而不是為每個(gè)任務(wù)創(chuàng)建新線程。這可以通過(guò)減少線程創(chuàng)建的開(kāi)銷來(lái)顯著提高應(yīng)用程序的性能。Java的ExecutorService和ThreadPoolExecutor類提供了管理線程池的框架。

關(guān)鍵點(diǎn)

  • 線程重用:線程池的線程可用于多個(gè)任務(wù)的重用。
  • 任務(wù)排隊(duì):任務(wù)被提交到池中,池中的線程會(huì)提取并執(zhí)行這些任務(wù)。
  • 資源管理:可配置線程池大小、任務(wù)隊(duì)列大小和其他參數(shù),以高效管理資源。

1. 使用線性池的原因

  • 性能:線程的創(chuàng)建和銷毀成本較高,尤其在Java中。創(chuàng)建一個(gè)可供多任務(wù)重用的線程池可減少這種開(kāi)銷。
  • 可擴(kuò)展性:線程池可根據(jù)應(yīng)用程序的需要進(jìn)行擴(kuò)展。例如,在負(fù)載較重時(shí),可擴(kuò)展線程池以處理額外的任務(wù)。
  • 資源管理:線程池可幫助管理線程使用資源。例如,線程池可限制同時(shí)活動(dòng)的線程數(shù)量,防止應(yīng)用程序內(nèi)存不足。

2. 確定線程池大小:理解系統(tǒng)和資源限制

了解包括硬件和外部依賴關(guān)系的系統(tǒng)限制,對(duì)于確定線程池大小至關(guān)重要。下面通過(guò)一個(gè)例子來(lái)詳細(xì)說(shuō)明這一概念。

假設(shè)在開(kāi)發(fā)一個(gè)處理傳入HTTP請(qǐng)求的Web應(yīng)用程序,每個(gè)請(qǐng)求可能涉及數(shù)據(jù)庫(kù)處理數(shù)據(jù)和調(diào)用外部第三方服務(wù)。目標(biāo)是確定處理這些請(qǐng)求的最佳線程池大小。

此情況下需考慮因素包含數(shù)據(jù)庫(kù)連接池與外部服務(wù)吞吐量?jī)煞矫妗?/p>

  • 數(shù)據(jù)庫(kù)連接池:假設(shè)使用HikariCP之類的連接池來(lái)管理數(shù)據(jù)庫(kù)連接。您已將其配置為允許最多100個(gè)連接。如果您創(chuàng)建的線程超過(guò)可用連接數(shù),這些額外的線程將會(huì)等待可用連接,導(dǎo)致資源爭(zhēng)用和潛在的性能問(wèn)題。

以下是配置HikariCP數(shù)據(jù)庫(kù)連接池的示例。

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class DatabaseConnectionExample {
    public static void main(String[] args) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        config.setUsername("username");
        config.setPassword("password");
        config.setMaximumPoolSize(100); // 設(shè)置最大連接數(shù)

        HikariDataSource dataSource = new HikariDataSource(config);

        // 使用 dataSource 獲取數(shù)據(jù)庫(kù)連接并執(zhí)行查詢。
    }
}
  • 外部服務(wù)吞吐量:此應(yīng)用程序交互的外部服務(wù)有一個(gè)限制。它一次只能處理少量請(qǐng)求,例如10個(gè)請(qǐng)求。并發(fā)更多請(qǐng)求可能會(huì)使服務(wù)不堪重負(fù),導(dǎo)致性能下降或出現(xiàn)錯(cuò)誤。CPU核心確定服務(wù)器上可用的CPU核心數(shù)量對(duì)于優(yōu)化線程池大小至關(guān)重要。
int numOfCores = Runtime.getRuntime().availableProcessors();

每個(gè)核心可以同時(shí)執(zhí)行一個(gè)線程。超出CPU核心數(shù)量的線程會(huì)導(dǎo)致過(guò)度的上下文切換,從而降低性能。

3.CPU密集型任務(wù)與I/O密集型任務(wù)

CPU密集型任務(wù)

CPU密集型任務(wù)是那些需要大量處理能力的任務(wù),例如執(zhí)行復(fù)雜計(jì)算或運(yùn)行模擬。這些任務(wù)通常受限于CPU的速度,而不是I/O設(shè)備的速度,如下列任務(wù)。

  • 編碼或解碼音頻或視頻文件
  • 編譯和鏈接軟件
  • 運(yùn)行復(fù)雜模擬
  • 執(zhí)行機(jī)器學(xué)習(xí)或數(shù)據(jù)挖掘任務(wù)
  • 玩視頻游戲

要對(duì)CPU密集型任務(wù)進(jìn)行優(yōu)化,應(yīng)考慮多線程和并行性。并行處理是一種技術(shù),用于將較大的任務(wù)劃分為較小的子任務(wù),并將這些子任務(wù)分配到多個(gè)CPU核心或處理器上,以利用并發(fā)執(zhí)行并提高整體性能。假設(shè)有一個(gè)大型數(shù)字?jǐn)?shù)組,要使用多個(gè)線程并發(fā)計(jì)算每個(gè)數(shù)字的平方,以利用并行處理,示例代碼如下。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ParallelSquareCalculator {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int numThreads = Runtime.getRuntime().availableProcessors(); // 獲取 CPU 核心數(shù)量
        ExecutorService executorService = Executors.newFixedThreadPool(numThreads);

        for (int number : numbers) {
            executorService.submit(() -> {
                int square = calculateSquare(number);
                System.out.println("Square of " + number + " is " + square);
            });
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    private static int calculateSquare(int number) {
        // 模擬耗時(shí)計(jì)算(例如,數(shù)據(jù)庫(kù)查詢、復(fù)雜計(jì)算)
        try {
            Thread.sleep(1000); // 模擬 1 秒延遲
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        return number * number;
    }
}

I/O密集型任務(wù)

I/O密集型任務(wù)是那些與存儲(chǔ)設(shè)備(例如,讀寫(xiě)文件)、網(wǎng)絡(luò)套接字(例如API調(diào)用)或用戶輸入進(jìn)行交互(例如圖形用戶界面的交互)的任務(wù),下面是一些典型的I/O密集型任務(wù)。

  • 讀取或?qū)懭氪笪募酱疟P(pán)(例如,保存視頻文件、加載數(shù)據(jù)庫(kù))
  • 通過(guò)網(wǎng)絡(luò)下載或上傳文件(例如,瀏覽網(wǎng)頁(yè)、觀看流媒體視頻)
  • 發(fā)送和接收電子郵件
  • 運(yùn)行Web服務(wù)器或其他網(wǎng)絡(luò)服務(wù)
  • 執(zhí)行數(shù)據(jù)庫(kù)查詢
  • Web服務(wù)器處理傳入請(qǐng)求

優(yōu)化I/O密集型任務(wù)的方式

  • 在內(nèi)存中緩存頻繁訪問(wèn)的數(shù)據(jù),以減少重復(fù)I/O操作的需要。
  • 負(fù)載均衡,將I/O密集型任務(wù)分配到多個(gè)線程或進(jìn)程中,以高效處理并發(fā)I/O操作。
  • 使用SSD,固態(tài)硬盤(pán)(SSDs)相較于傳統(tǒng)硬盤(pán)(HDDs)可以顯著加快I/O操作。
  • 使用高效數(shù)據(jù)結(jié)構(gòu),如哈希表和B樹(shù),以減少所需的I/O操作次數(shù)。
  • 避免不必要的文件操作,例如多次打開(kāi)和關(guān)閉文件。

4.在兩種任務(wù)中確定線程數(shù)

確定CPU密集型任務(wù)的線程數(shù)量

對(duì)于CPU密集型任務(wù),要最大化CPU利用率,而不讓系統(tǒng)因線程過(guò)多而超負(fù)荷,防止過(guò)度的上下文切換。一個(gè)常見(jiàn)方法是使用可用CPU核心數(shù)量。假設(shè)需開(kāi)發(fā)一個(gè)視頻處理應(yīng)用程序。視頻編碼是一項(xiàng)CPU密集型任務(wù),需要應(yīng)用復(fù)雜算法來(lái)壓縮視頻文件。有一個(gè)多核CPU可用。

  • 計(jì)算可用CPU核心:使用Runtime.getRuntime().availableProcessors()確定可用的CPU核心數(shù)量。假設(shè)有8個(gè)核心。
  • 創(chuàng)建線程池:創(chuàng)建一個(gè)大小接近或略少于可用CPU核心數(shù)量的線程池。在這種情況下,您可以選擇6或7個(gè)線程,以便為其他任務(wù)和系統(tǒng)進(jìn)程留出一些CPU容量。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VideoEncodingApp {
    public static void main(String[] args) {
        int availableCores = Runtime.getRuntime().availableProcessors();
        int numberOfThreads = Math.max(availableCores - 1, 1); // 根據(jù)需要進(jìn)行調(diào)整

        ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);

        // 將視頻編碼任務(wù)提交到線程池。
        for (int i = 0; i < 10; i++) {
            threadPool.execute(() -> {
                encodeVideo(); // 模擬視頻編碼任務(wù)
            });
        }

        threadPool.shutdown();
    }

    private static void encodeVideo() {
        // 模擬視頻編碼(CPU 密集型)任務(wù)。
        // 這里進(jìn)行復(fù)雜計(jì)算和壓縮算法。
    }
}

確定I/O密集型任務(wù)的線程數(shù)量

對(duì)于I/O密集型任務(wù),最佳線程數(shù)通常由I/O操作的性質(zhì)和預(yù)期延遲決定。您希望擁有足夠的線程以保持I/O設(shè)備繁忙而不使其過(guò)載。理想的數(shù)量不必等于CPU核心的數(shù)量??紤]構(gòu)建一個(gè)網(wǎng)頁(yè)爬蟲(chóng),下載網(wǎng)頁(yè)并提取信息。這涉及HTTP請(qǐng)求,這是因網(wǎng)絡(luò)延遲引起的I/O密集型任務(wù),可從如下兩方面進(jìn)行分析。

  • 分析I/O延遲:估計(jì)預(yù)期的I/O延遲,這依賴于網(wǎng)絡(luò)或存儲(chǔ)。例如,如果每個(gè) HTTP 請(qǐng)求大約需要500毫秒完成,您可能需要考慮I/O操作中的一些重疊。
  • 創(chuàng)建線程池:創(chuàng)建一個(gè)大小平衡并行性與預(yù)期I/O延遲的線程池。您不一定需要為每個(gè)任務(wù)分配一個(gè)線程;相反,可以有一個(gè)較小的池,能夠高效管理I/O密集型任務(wù)。

以下是網(wǎng)頁(yè)爬蟲(chóng)的示例代碼。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WebPageCrawler {
    public static void main(String[] args) {
        int expectedIOLatency = 500; // 估計(jì)的 I/O 延遲(毫秒)
        int numberOfThreads = 4; // 根據(jù)預(yù)期延遲和系統(tǒng)能力進(jìn)行調(diào)整

        ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);

        // 要抓取的 URL 列表。
        String[] urlsToCrawl = {
            "https://example.com",
            "https://google.com",
            "https://github.com",
            // 在這里添加更多 URL
        };

        for (String url : urlsToCrawl) {
            threadPool.execute(() -> {
                crawlWebPage(url, expectedIOLatency);
            });
        }

        threadPool.shutdown();
    }

    private static void crawlWebPage(String url, int expectedIOLatency) {
        // 模擬網(wǎng)頁(yè)抓?。↖/O 密集型)任務(wù)。
        // 執(zhí)行 HTTP 請(qǐng)求并處理頁(yè)面內(nèi)容。
        try {
            Thread.sleep(expectedIOLatency); // 模擬 I/O 延遲
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

5.總結(jié)公式

確定線程池大小的公式可以寫(xiě)成如下形式。

線程數(shù)=可用核心數(shù)*目標(biāo)CPU利用率*(1+等待時(shí)間/服務(wù)時(shí)間)

該公式各部分的詳細(xì)解釋如下。

  • 可用核心數(shù):這是您的應(yīng)用程序可用的 CPU 核心數(shù)量。重要的是要注意,這與 CPU 的數(shù)量不同,因?yàn)槊總€(gè) CPU 可能有多個(gè)核心。
  • 目標(biāo)CPU利用率:這是您希望應(yīng)用程序使用的 CPU 時(shí)間的百分比。如果將目標(biāo)CPU利用率設(shè)置得過(guò)高,應(yīng)用程序可能會(huì)變得無(wú)響應(yīng);如果設(shè)置得太低,應(yīng)用程序?qū)o(wú)法充分利用可用CPU資源。
  • 等待時(shí)間:這是線程等待I/O操作完成的時(shí)間。這可能包括等待網(wǎng)絡(luò)響應(yīng)、數(shù)據(jù)庫(kù)查詢或文件操作的時(shí)間。
  • 服務(wù)時(shí)間:這是線程執(zhí)行計(jì)算的時(shí)間。
  • 阻塞系數(shù):這是等待時(shí)間與服務(wù)時(shí)間的比率。它是衡量線程在 I/O 操作完成之前等待的時(shí)間相對(duì)于執(zhí)行計(jì)算時(shí)間的比例。

示例使用

假設(shè)有一個(gè)具有4個(gè)CPU核心的服務(wù)器,并且希望應(yīng)用程序使用50%的可用 CPU資源。

您的應(yīng)用程序有兩個(gè)任務(wù)類別:I/O密集型任務(wù)和CPU密集型任務(wù)。

I/O密集型任務(wù)的阻塞系數(shù)為0.5,意味著它們花費(fèi)50%的時(shí)間等待I/O 操作完成。

線程數(shù)=4核心*0.5*(1+0.5)=3線程

CPU 密集型任務(wù)的阻塞系數(shù)為 0.1,意味著它們花費(fèi) 10% 的時(shí)間等待 I/O 操作完成。

線程數(shù)=4核心*0.5*(1+0.1)=2.2線程

此示例創(chuàng)建了兩個(gè)線程池,一個(gè)用于I/O密集型任務(wù),一個(gè)用于CPU密集型任務(wù)。I/O密集型線程池將有3個(gè)線程,CPU密集型線程池將有2個(gè)線程。

責(zé)任編輯:武曉燕 來(lái)源: Java學(xué)研大本營(yíng)
相關(guān)推薦

2010-03-18 15:15:08

Java線程池

2019-09-09 09:50:27

設(shè)置Java線程池

2023-08-04 11:04:03

線程池項(xiàng)目開(kāi)發(fā)

2010-03-15 16:56:16

Java線程池

2012-05-15 02:18:31

Java線程池

2023-09-08 12:19:01

線程方法interrupt

2025-02-06 03:14:38

2024-03-15 09:32:47

線程池應(yīng)用程序性能

2021-09-23 15:55:50

線程池語(yǔ)言公式

2021-09-11 15:26:23

Java多線程線程池

2015-08-20 09:17:36

Java線程池

2023-06-08 07:48:03

Java線程池

2022-02-28 07:01:22

線程中斷interrupt

2021-06-24 08:02:35

線程池Java代碼

2023-11-22 08:37:40

Java線程池

2010-02-24 10:24:10

Python線程

2022-03-30 08:54:21

線程 Thread判斷線程池任務(wù)Java

2021-11-15 10:50:52

Java線程池代碼

2023-07-05 07:48:04

線程池join關(guān)閉狀態(tài)

2012-02-29 13:26:20

Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)