5種類のjavaスレッドプールの使用と比較

9449 ワード

今日は、一般的なjava内蔵スレッドプールの5つについて説明します.
スレッドで使用するdemo
public static void cache() {
        ExecutorService pool = Executors.newCachedThreadPool();
        long start = System.currentTimeMillis();
        pool.execute(() -> {
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                sum = (int) Math.sqrt(i * i - 1 + i);
                System.out.println(sum);
            }
        });
        System.out.println("cache: " + (System.currentTimeMillis() - start));
    }

newCachedThreadPool
  • キャッシュ可能スレッドプールで、空きスレッドを柔軟に回収でき、リサイクル可能スレッドがない場合、新しいスレッド
  • を作成します.
    ソース:
    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue());
        }

    ソースコードから、最下位レベルで呼び出されたのはThreadPoolExecutorメソッドであり、同期されたブロックキューに転送されてキャッシュが実現されることがわかります.
    次にThreadPoolExecutorについてお話しします
    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue workQueue) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory(), defaultHandler);
        }

    ソースコードから,スレッドプールに転送できるコアスレッド数(最小スレッド数),最大スレッド数,保持時間,時間単位,ブロックキューなどのパラメータ,最大スレッド数をjvmで利用可能なcpu数に設定することがベストプラクティスであることが分かった.
    newWorkStealingPool
  • は、パラレルに十分なスレッドを持つスレッドプールを作成し、複数のキューを使用することで競合を低減し、パラメータを送信しない場合、デフォルトではcpuの数
  • に設定する.
    ソース:
    public static ExecutorService newWorkStealingPool() {
            return new ForkJoinPool
                (Runtime.getRuntime().availableProcessors(),
                 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                 null, true);
        }

    ソースコードから、最下位の呼び出しはForkJoinPoolスレッドプールであることがわかります.
    次にForkJoinPoolについてお話しします
    public ForkJoinPool(int parallelism,
                            ForkJoinWorkerThreadFactory factory,
                            UncaughtExceptionHandler handler,
                            boolean asyncMode) {
            this(checkParallelism(parallelism),
                 checkFactory(factory),
                 handler,
                 asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
                 "ForkJoinPool-" + nextPoolId() + "-worker-");
            checkPermission();
        }

    無限キューを使用して、実行するタスクを保存します.スレッドの数を入力できます.入力しない場合は、デフォルトでは、現在のコンピュータで使用可能なcpuの数を使用し、分治法を使用して問題を解決し、fork()とjoin()を使用して呼び出します.
    newSingleThreadExecutor
    スレッド化されたスレッドプールを作成し、すべてのタスクが指定された順序で実行されることを保証します(FIFO、LIFO、優先度).プロセスの制限が必要な場合、使用できます.
    ソース:
    public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue()));
        }

    newFixedThreadPool
    固定スレッド数、再利用可能なスレッドプールの作成
    ソース:
    public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue());
        }

    newScheduledThreadPool
    定期的または遅延的にタスクを実行できるスレッドプールの作成
    ソース:
    return new ScheduledThreadPoolExecutor(corePoolSize);

    ソースコードから、下位レベルで呼び出されたのはScheduledThreadPoolExecutorであり、その後、スレッド数が入力されることがわかります.
    次に、ScheduledThreadPoolExecutorについて説明します.
    public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                  new DelayedWorkQueue());
        }

    ソースコードにより、最下位レベルでThreadPoolExecutorが呼び出され、遅延キューが維持され、スレッド数、受信遅延時間などのパラメータが入力されることがわかります.以下にdemoを示します.
    public static void main(String[] args) {
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
            for (int i = 0; i < 15; i = i + 5) {
                pool.schedule(() -> System.out.println("     ,    " + new Date()), i, TimeUnit.SECONDS);
            }
            pool.shutdown();
        }

    実行結果
    Fri Jan 12 11:20:41 CST 2018
         ,    Fri Jan 12 11:20:46 CST 2018
         ,    Fri Jan 12 11:20:51 CST 2018

    submit()やexecute()ではなくschedule()を使用する理由に疑問を持つ仲間もいるかもしれませんが、ソースコードで分析します.
        public void execute(Runnable command) {
            schedule(command, 0, NANOSECONDS);
        }
        public Future> submit(Runnable task) {
            return schedule(task, 0, NANOSECONDS);
        }

    ソースコードから、両方のメソッドが呼び出しのschedule()であり、遅延時間を0に設定していることがわかります.したがって、遅延操作を実現するには、schedule()を直接呼び出す必要があります.
    次にsubmit()とexecute()の違いとshutdown()とshutdown Now()の違いを分析します
  • submit()は、スレッドタスクをコミットし、コールバック関数の戻り値を受け入れることができますか?戻りまたは異常を処理する必要があるビジネスシーン
  • に適用されます.
  • execute()は、
  • の値を返さずにタスクを実行します.
  • shutdown()は、新しいタスクを受け入れないが、コミット済みまたは実行中のタスク
  • を強制的に終了しないことを示す.
  • shutdownNow()は、まだ実行されていないタスクに対してすべてキャンセルし、実行中のタスクに対してすべてinterrupt()を発行し、
  • の実行を停止する.
    5つのスレッドプールの適応シーン
  • newCachedThreadPool:サーバ負荷が軽く、多くの短期非同期タスクを実行するために、無限に拡大可能なスレッドプールを作成します.
  • newFixedThreadPool:境界のないブロックキューを使用するため、スレッドの数は常に変化しない固定サイズのスレッドプールを作成します.スレッドの数を予測できるビジネスや、サーバの負荷が重く、現在のスレッドの数を制限します.
  • newSingleThreadExecutor:単一スレッドのスレッドプールを作成し、各タスクを順番に実行する必要があり、任意の時点で複数のスレッドがアクティブなシーンにならないようにします.
  • newScheduledThreadPool:起動を遅らせることができ、タイミング的に起動するスレッドプールは、複数のバックグラウンドスレッドがサイクルタスクを実行する必要があるシーンに適しています.
  • newWorkStealingPool:複数のタスクキューを持つスレッドプールを作成し、接続数を減らすことができ、現在使用可能なcpu数のスレッドを作成して並列に実行することができ、大消費時の操作に適しており、
  • を並列に実行することができる.
    以上が今日共有したい内容ですが、同時接触が深くないため、エラーがあれば、ブロガーに連絡してください.
    広大な星の中のちっぽけな私达、世界を変える梦想を持っています