Javaのスレッドプールについて話します

4984 ワード

スレッドプールとは?
スレッドプールは、作業スレッドのセットが保存されているコンテナで、タスクが来るたびにコンテナから使用可能なスレッドを取り出してタスクを実行します.
スレッドプールの役割?
  • スレッドプールは、頻繁なスレッドの作成、削除が必要なため、パフォーマンスオーバーヘッド
  • を削減するため、大量の非同期タスクを実行する際に顕著なパフォーマンス向上をもたらす.
  • は、システムの安定性
  • を保障するためのリソース制御可能な方法を提供する.
  • は、完了したタスク数
  • のようなスレッドプールの基礎状態データを取得する.
    リソース制御vs安定性?
    なぜリソース制御とシステムの安定したパフォーマンスが関連しているのでしょうか.ここで例を挙げると、Tomcatがスレッドプールを使用していない場合、要求が入るたびにスレッドが作成されます.10000スレッドが同時に作成されている場合は、スレッドの作成に問題はありません.結局、各スレッドのスタックスペースは大きくありませんが、10000リクエストを同時に処理するのは冗談です.ほとんどの時間はCPUがコンテキスト切り替えをしている可能性があります.資源を浪費している.ここでスレッドプールが機能し始め、同時に処理されるタスク数を保証することができます.例えば、100個です.この時、資源の制御可能、システムの安定性を保障する役割を果たしたのではないでしょうか.
    スレッドプールの種類
    jdk中線程池には多くの種類があり、作用はすべて異なり、具体的には以下の通りである.
  • 固定サイズスレッドプール:newFixedThreadPool
  • 単一スレッドプール:newSingleThreadExecutor、複数のタスクが到着し、タスク実行中にスレッドが切れた場合、スレッドプールは後続のタスクを完了するために新しいスレッドを再作成します.スレッドプールにいつまでも1つのスレッドしかないことを保証するだけです.
  • キャッシュスレッドプール:newCachedThreadPool、どれだけのタスクが来て、私はどれだけのスレッドを作成して処理して、処理が終わった後に急いでスレッドを回収しないで、スレッドが60 sアイドルになった後に、自動的にスレッドを回収して、もしスレッドプールの中で1つのタスクがすべてないならば、そのスレッドプールはアクティブなスレッドがありません.
  • 単一スレッドスケジューリングスレッドプール:newSingleThreadScheduledExecutor.名前の通り、スレッドプールにはスレッドが1つしかなく、定期的にタスクを実行します.
  • マルチスレッドスケジューリングスレッドプール: .上記と同様に、タイミングタスクスレッドが構成できるだけです.
  • 盗作スレッドプール:newWorkStealingPool.主な処理ForkJoinTask
  • スレッドプールのパラメータの意味は?
    実は上にこんなに多くのスレッドプールがあり、下層処理クラスはThreadPoolExecutorで、このクラスを理解すればスレッドプールの原理も理解できます.もちろん、この問題はよく面接に出ますが、どうせ私は前に失敗したことがあるので、ここで記録しておきましょう.まず、完了したコンストラクション関数を見てみましょう.
    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler)
    
  • corePoolSizeパラメータは、スレッドプールにタスクが実行されているかどうかにかかわらず、corePoolSize個のスレッドが存在することを示し、0newCachedThreadPoolに設定することができる.allowCoreThreadTimeOutが設定されている場合、スレッドプールは、前述のcorePoolSizeスレッド
  • を含む、タスクがないことを発見した場合、アイドルスレッドをすべて停止します.
  • maximumPoolSizeはスレッドプールが同時に処理できる最大スレッド数を表し、同時に処理できる最大タスク数としても理解できるが、この数を超えるとタスクはworkQueueに並ぶ
  • となる.
  • keepAliveTimeは、スレッドプール内のスレッドタスクが実行すると、どのくらいアイドルになっているかを示す
  • が回収される.
  • unitアイドル時間に対応する単位
  • workQueueタスク数がmaximumPoolSizeを操作すると、後から提出されたタスクはworkQueueに入って並びます.キューサイズを指定すると、10を設定すると、キューがいっぱいになると、後から提出されたタスクはスレッドプールに拒否されます.ボディがどのように拒否するかは、後のhandlerパラメータ
  • を参照してください.
  • threadFactoryは、スレッドを作成するファクトリクラスを表します.指定しなくてもよく、デフォルトではExecutors.DefaultThreadFactoryが使用されます.スレッドプールによって作成されるスレッドのデフォルトはpool-poolNumber-thread-threadNumberです.たとえば、pool-1-thread-2
  • です.
  • handlerは、コミットされたタスクがmaximumPoolSize + workQueue.size()を超えると、jvmが後続のコミットされたタスクをどのように処理するかを示す.具体的な拒否ポリシーは、次の
  • を参照する.
    スレッドプールの拒否ポリシー?
    拒否ポリシーは4つあります.ThreadPoolExecutor.AbortPolicy
    直接mainスレッドの中でRejectedExecutionExceptionの異常を投げ出して、これは1つの運行時の異常で、やはりとても恐ろしくて、mainスレッドは掛けて、全体の応用はすべて掛けて、だからきっと慎重にしなければなりません!ThreadPoolExecutor.DiscardPolicy
    提出を続けてもいいですが、私は処理しません.後続の任務が処理できるかどうかは縁次第だ.スレッドプール内のタスクが完了し、ちょうどタスクがコミットされた場合、このタスクは実行できます.後続の提出はまた直接なくした.ThreadPoolExecutor.DiscardOldestPolicy
    スレッドプールに最初にコミットされたが実行されていないタスクを捨てます.つまり、このタスクはまだ先頭に立っていますが、なぜかpollに落ち、現在コミットされているタスクをキューに追加します.ThreadPoolExecutor.CallerRunsPolicy
    後でスレッドプールの範囲を超えたタスクを提出したら、私は管理したくないので、自分のスレッドで実行しましょう.つまり、スレッドプールの管理から離れています!JavaのデフォルトポリシーはAbortPolicyです
    独自のスレッドプールの実装
    package cn.crabime;
    
    import java.util.concurrent.*;
    
    /**
     *          
     */
    public class FixedBoundThreadPool {
    
        public static void main(String[] args) {
            BlockingQueue blockingQueue = new LinkedBlockingQueue<>(10);
            ThreadPoolExecutor executor =
                    new ThreadPoolExecutor(1, 5, 2000, TimeUnit.MILLISECONDS, blockingQueue, new ThreadPoolExecutor.AbortPolicy());
    
            //                     
            executor.allowCoreThreadTimeOut(true);
    
            //     100   ,          
            for (int i = 0; i < 100; i++) {
                executor.submit(() -> {
                        System.out.println(Thread.currentThread().getName() + "      ");
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "      ");
    
                });
            }
            executor.shutdown();
        }
    }