マルチスレッド スレッド プールの焦点については後で検討します

スレッドプールの利点

全体として、スレッド プールには次の利点があります。
(1) リソース消費を削減します。作成されたスレッドを再利用することで、スレッドの作成と破棄のコストを削減します。
(2) 応答速度の向上。タスクが到着すると、スレッドの作成を待たずにすぐにタスクを実行できます。
(3) スレッドの管理性を向上させます。スレッドは希少なリソースです。無制限に作成すると、システム リソースを消費するだけでなく、システムの安定性も低下します。スレッド プールを使用すると、一元的な割り当て、チューニング、監視が可能になります。

4 つのパラメータを使用して設計します。

corePoolSize (必須) : コアスレッドの数。デフォルトでは、コア スレッドは常にアクティブですが、allowCoreThreadTimeout が true に設定されている場合、コア スレッドも時間の経過とともにリサイクルされます。
minimumPoolSize (必須) : スレッド プールが保持できるスレッドの最大数。アクティブなスレッドの数がこの値に達すると、後続の新しいタスクはブロックされます。
keepAliveTime (必須) : スレッドのアイドル タイムアウト。この時間を超えると、非コアスレッドがリサイクルされます。allowCoreThreadTimeout が true に設定されている場合、コア スレッドはタイムアウト後にもリサイクルされます。
単位 (必須) : keepAliveTime パラメーターの時間単位を指定します。一般的に使用されるのは、TimeUnit.MILLISECONDS (ミリ秒)、TimeUnit.SECONDS (秒)、TimeUnit.MINUTES (分) です。
workQueue (必須) : タスクキュー。スレッドプールのexecute()メソッドによって送信されたRunnableオブジェクトは、このパラメータに格納されます。これはブロッキング キューを使用して実装されます。
threadFactory (オプション) : スレッド ファクトリ。スレッド プールに新しいスレッドを作成する方法を指定するために使用されます。
ハンドラー (オプション) : 拒否ポリシー。スレッドの最大数に達したときに実行される飽和戦略。

スレッド プールを作成する 4 つの方法:

newCachedThreadPool は
、キャッシュ可能なスレッド プールを作成します。スレッド プールの長さが処理の必要性を超える場合、アイドル状態のスレッドは柔軟にリサイクルできます。リサイクルがない場合、新しいスレッド newFixedThreadPool は、指定された数のワーカー スレッドを持つスレッド プールを作成し
ます
タスクが送信されるたびにワーカー スレッドが作成され、ワー​​カー スレッドの数がスレッド プールの初期最大数に達すると、送信されたタスクはプール キューに格納されます。
newSingleThreadExecutor
は、シングルスレッドの Executor を作成します。つまり、タスクを実行するための一意のワーカー スレッドのみを作成し、タスクの実行に唯一のワーカー スレッドのみを使用して、すべてのタスクが指定された順序 (FIFO、LIFO、優先順位) で実行されるようにします。 。このスレッドが異常終了すると、別のスレッドが代わりに実行され、順次実行が保証されます。
newScheduledThreadPool は
、固定長のスレッド プールを作成し、タイミングと定期的なタスクの実行をサポートし、タイミングと定期的なタスクの実行をサポートします。

拒否ポリシー (ハンドラー)

スレッド プールの拒否戦略とは、スレッド プール内のすべてのスレッドが占有されている場合に新しいタスク要求を処理する方法を指します。一般的なスレッド プールの拒否戦略は 4 つあります。

  1. AbortPolicy (デフォルトのポリシー): RejectedExecutionException を直接スローして、システムが正常に実行されないようにする。
  2. CallerRunsPolicy : 拒否されたタスクをメイン スレッドで直接実行します。タスクの送信が速すぎると、メイン スレッドが過負荷になり、システムの通常の動作に影響を与える可能性があります。
  3. DiscardPolicy : 拒否されたタスクを処理せずに直接破棄します。
  4. DiscardOldestPolicy : タスク キュー内の最も古いタスクを破棄し、タスク送信操作を再試行します。スレッドプールの容量が最大値に達した場合、これらの破棄されたタスクは実行されません。

最適なソリューションは、アプリケーションの要件とシナリオによって異なります。一般に、特定のニーズを満たすにはカスタム拒否ポリシーを使用することをお勧めします。
以下に、考えられるカスタム拒否ポリシーをいくつか示します。

  1. キューの容量には制限があり、キューがいっぱいになると、タスクはキューに入れられずに直接実行されます。この戦略により、タスクは破棄されなくなりますが、メインスレッドに過度の負荷がかかる可能性があります。
  2. 適応的な拒否戦略を実装します。スレッド プール内のスレッド数が最大値に達した場合は、スレッド プールの最大容量を徐々に増やし、拒否戦略の頻度を減らして、タスク リクエストをより適切に処理します。
  3. タスクを分散タスク キューに入れると、タスクを複数のノードに分散して実行できるようになり、全体のスループットが向上します。

最終的な最善の解決策は、特定のビジネス ニーズとシナリオに基づいて適切な拒否戦略を選択し、適切な調整と最適化を行うことです。

実装プロセス

画像.png

サンプルコード:

thread:
    # 核心线程池数
    corePoolSize:
    # 最大线程池数
    maxPoolSize:
    # 任务队列的容量
    queueCapacity:
    # 非核心线程的存活时间
    keepAlive:
    # 创建线程的等待时间
    awaitTerminationSeconds:
package com.demo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池
 */
@Configuration
public class ThreadPoolTaskExecutorConfig implements AsyncConfigurer {
    
    

    // 当前机器核数
    public static final int cpuNum = Runtime.getRuntime().availableProcessors();

    @Value("${thread.corePoolSize}")
    private Integer corePoolSize;

    @Value("${thread.maxPoolSize}")
    private Integer maxPoolSize;

    @Value("${thread.queueCapacity}")
    private Integer queueCapacity;

    @Value("${thread.keepAlive}")
    private Integer keepAlive;

    @Value("${thread.awaitTerminationSeconds}")
    private Integer awaitTerminationSeconds;

    @Override
    @Bean
    public Executor getAsyncExecutor() {
    
    
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程池数
        threadPoolTaskExecutor.setCorePoolSize(optionl(corePoolSize, cpuNum));
        //最大线程池数
        threadPoolTaskExecutor.setMaxPoolSize(optionl(maxPoolSize, cpuNum * 2));
        //任务队列的容量
        threadPoolTaskExecutor.setQueueCapacity(optionl(queueCapacity, 3));
        //非核心线程的存活时间-最大空闲时间
        threadPoolTaskExecutor.setKeepAliveSeconds(optionl(keepAlive, 10));
        threadPoolTaskExecutor.setThreadNamePrefix("test-thread-");
        // 抛异常规则
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        threadPoolTaskExecutor.initialize();
        // 创建线程的等待时间
        threadPoolTaskExecutor.setAwaitTerminationSeconds(optionl(awaitTerminationSeconds, 10));
        return threadPoolTaskExecutor;
    }

    /**
     * 判空-替换
     *
     * @param key  原值
     * @param key2 替换值
     * @return 结果
     */
    public Integer optionl(Integer key, Integer key2) {
    
    
        // 如果key为空则赋值为key2
        return Optional.ofNullable(key).orElse(key2);
    }

}

public static void main(String[] args) {
    
    
        ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig = new ThreadPoolTaskExecutorConfig();
        // 创建线程池
        Executor executor = threadPoolTaskExecutorConfig.getAsyncExecutor();
        executor.execute(()->{
    
    
            System.out.println("内容");
        });
    }

参考:

ラグランジュ
スレッド プールを作成する4 つの方法

おすすめ

転載: blog.csdn.net/weixin_44824381/article/details/131088209