ジャワラインシティの詳しい説明

1. ラインシティの紹介

(1) システム リソースの消費を削減し、既存のスレッドを再利用することにより、スレッドの作成と破棄による消費を削減します。

(2) システムの応答速度の向上 タスクが到着すると、既存のスレッドを再利用することで、新しいスレッドの作成を待たずにすぐに実行できます。

(3) 同時スレッド数を制御できると便利です。スレッドを無制限に作成すると、過剰なメモリ使用による OOM が発生する可能性があり、過度の CPU 切り替えが発生する可能性があるため (CPU 切り替えスレッドには時間コストがかかる (現在の実行スレッドの場所を維持し、実行スレッドを復元する必要がある)サイト) )

(4) より強力な機能、遅延タイミング スレッド プールを提供します。

2. ラインシティの作り方

Executors は、concurrent パッケージの下のクラスであり、スレッド プールを作成する簡単な方法を提供します。

Executor は、一般的に使用される 4 つのスレッド プールを作成できます。

(1) newCachedThreadPool はキャッシュ可能なスレッド プールを作成します. スレッド プールの長さが処理の必要性を超える場合, アイドル状態のスレッドを柔軟にリサイクルできます. リサイクル可能なスレッドがない場合, 新しいスレッドが作成されます. 上限はなく、提出されたタスクはすぐに実行されます。

(2) newFixedThreadPool は、同時スレッドの最大数を制御できる固定長のスレッド プールを作成し、余分なスレッドはキューで待機します。

(3) newScheduledThreadPool は、タイミングと定期的なタスク実行をサポートする固定長のスレッド プールを作成します。

(4) newSingleThreadExecutor は、タスクを実行するためのシングルスレッド スレッド プールを作成します。

しかし、上記の 4 つの作成方法には非常に不利な点があります。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

アリの仕様によると許可されていません

画像-20220413222641239

さらに、LinkedBlockingQueue は、キューのサイズが無制限のキューであることを指定していないため、oom が発生する可能性があります。

3.カスタムラインシティ

ThreadPoolExecutor クラスで最も多くのパラメーターを持つコンストラクター:

orePoolSize:スレッド プール内のコア スレッドの数. スレッド プールが初期化されると、コア スレッドが作成され、待機状態になります. アイドル状態であっても、コア スレッドが破棄されないため、時間とタスクが発生したときに新しいスレッドを作成するコスト パフォーマンスのオーバーヘッド。allowCoreThreadTimeOut を手動で true に設定すると、破棄されます

maximumPoolSize:スレッドの最大数。つまり、コア スレッドの数が使い果たされた場合、タスクを実行するために新しいスレッドのみを再作成できますが、スレッドの最大数を超えてはならないことが前提です。それ以外の場合、タスクはスレッドがアイドル状態のときのみ、タスクを続行できます。

keepAliveTime:スレッドの存続時間。コア スレッドを除き、新しく作成されたスレッドが存続できる時間。これは、これらの新しいスレッドがタスクを完了してアイドル状態になると、一定期間後に破棄されることを意味します。

**threadFactory: ** は、スレッドを作成するスレッド ファクトリです。

unit:スレッドの生存時間 unit
workQueue:タスクのブロッキング キューを示します. タスクが多く、スレッドが少ない場合があるため、実行されていないタスクがキューに入ります. キューが FIFO であることがわかっています. Waitスレッドがアイドル状態になるまで、タスクはこの方法で取り出されます。通常、これを実装する必要はありません。ハンドラーには 4 つの拒否戦略があります。

(1) AbortPolicy: 新しいタスクを実行せず、スレッド プールがいっぱいであることを示す例外を直接スローします
  (2) DisCardPolicy: 新しいタスクを実行せず、例外をスローしません
  (3) DisCardOldSetPolicy: 最初のタスクを置き換えますメッセージ キュー Execute
  (4) 現在の新しい着信タスクの CallerRunsPolicy : execute を直接呼び出して、現在のタスクを実行します。

よく使うキュー

\1. LinkedBlockingQueue
  FixedThreadPool と SingleThreadExector の場合、それらが使用するブロッキング キューは、無限キューと見なすことができる Integer.MAX_VALUE の容量を持つ LinkedBlockingQueue です。FixedThreadPool スレッド プールのスレッド数は固定であるため、タスクを処理するために特に多くのスレッドを追加する方法はありません. このとき、タスクを格納するために LinkedBlockingQueue のような容量制限のないブロッキング キューが必要です. ここで、スレッド プールのタスク キューがいっぱいになることはないため、スレッド プールはコア スレッドの数だけスレッドを作成することに注意してください。この時点でのスレッドの最大数は、スレッド プールにとって意味がありません。複数のスレッドの生成をトリガーすることはありません. コアスレッドの数に基づくスレッド.
\2. SynchronousQueue
  2 番目のブロッキング キューは SynchronousQueue で、対応するスレッド プールは CachedThreadPool です。スレッドプール CachedThreadPool の最大スレッド数は Integer の最大値であり、無限にスレッド数を拡張できることがわかります。CachedThreadPool は、従来のスレッド プールである FixedThreadPool の正反対です. FixedThreadPool の場合、ブロッキング キューの容量は無制限です. ここで、CachedThreadPool 内のスレッドの数は無限に拡張できるため、CachedThreadPool スレッド プールはスレッド プールを必要としません。タスクを保存するためのタスクキュー。タスクが送信されると、スレッドに直接転送されるか、個別に保存せずに実行する新しいスレッドが作成されます。
SynchronousQueue を使用するスレッド プールを作成する場合、タスクを拒否したくない場合は、最大スレッド数をできるだけ大きく設定するように注意し、タスク数が多いときにそれを回避する必要があります。スレッドの最大数を超えると、タスクをキューに入れる方法がありません. タスクを実行するのに十分なスレッドがない状況.
\3.DelayedWorkQueue
  3 つ目のブロッキング キューは DelayedWorkQueue. それに対応するスレッド プールは ScheduledThreadPool と SingleThreadScheduledExecutor です. この 2 つのスレッド プールの最大の特徴は、一定時間後にタスクを実行したり、定期的にタスクを実行したりするなど、タスクの実行を遅らせることができることです。 . . DelayedWorkQueue の特徴は、内部要素を入れた時間でソートするのではなく、遅延の長さでタスクをソートし、内部で使用するのは「ヒープ」のデータ構造です。スレッド プールの ScheduledThreadPool と SingleThreadScheduledExecutor が DelayedWorkQueue を選択する理由は、それら自体が時間に基づいてタスクを実行し、遅延キューはタスクの実行を容易にするために時間でタスクを並べ替えることができるためです。

画像-20220413223904792

4.スレッドプールの設定方法

CPU を集中的に使用するタスクの場合は、
通常、CPU コアの数 + 1 に等しい小さいスレッド プールを使用してみてください。CPU を集中的に使用するタスクは CPU 使用率を非常に高くするため、開いているスレッドが多すぎると、過度の CPU 切り替えが発生します。

IO 集中型のタスクは、
わずかに大きなスレッド プール (通常は 2*CPU コア数) を使用できます。IO 集中型タスクの CPU 使用率は高くないため、CPU は IO を待機している間、他のタスクを処理するために他のスレッドを持つことができ、CPU 時間を最大限に活用できます。

混合タスクでは、
タスクを IO 集中型タスクと CPU 集中型タスクに分割し、異なるスレッド プールを使用してそれらを処理できます。分割後の 2 つのタスクの実行時間に大きな差がない限り、シリアル実行よりも効率的です。
分割後の2つのタスクの実行時間にデータレベルのギャップがある場合、分割は意味がないからです。
最初に実行されるタスクは後で実行されるタスクを待たなければならないため、最終的な時間は後で実行されるタスクに依存し、タスクの分割とマージのオーバーヘッドが追加されます。これはろうそくの価値がありません。

5.ラインシティツール

public final class ThreadPoolProvider {
   private ThreadPoolProvider(){}

    /**
     * 线程池<br/>
     * 备注:不建议随处创建,可统一一处调用
     */
private static final ExecutorService FIXED_THREAD_POOL = new ThreadPoolExecutor(10,500,5,TimeUnit.SECONDS,
                                                                new  ArrayBlockingQueue<Runnable> (1000),
                                                                new DefaultThreadFactory(),
                                                                new ThreadPoolExecutor.AbortPolicy());

   /**
     * @Title newFixedThreadPool
     * @Description: 获取定长的线程池
     * @return  ExecutorService
     */
    public static ExecutorService newFixedThreadPool(){
        return FIXED_THREAD_POOL;
    }


    /**
     * @ClassName: DefaultThreadFactory
     * @Description: 线程命名工厂
     */
    private static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        DefaultThreadFactory() {
            this("thread-pool-t-");
        }
        DefaultThreadFactory(String prefix) {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            namePrefix = prefix+poolNumber.getAndIncrement();
        }
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon()) t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }
}

WeChat パブリック
アカウントここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/CharlesYooSky/article/details/124160264