Javaマルチスレッド10:カスタムスレッドプールThreadPoolExecutor

はじめに:Alibabaの619java開発マニュアルの新しいバージョンにはこのような段落があります

1. [必須]スレッドまたはスレッドプールを作成するときは、エラーが発生した場合のトレースバックを容易にするために、意味のあるスレッド名を指定してください。
2. [必須]スレッドリソースはスレッドプールを介して提供する必要があり、アプリケーションで明示的にスレッドを作成することは許可されていません。
注:スレッドプールの利点は、スレッドの作成と破棄にかかる時間とシステムリソースのオーバーヘッドを削減し、リソース不足の問題を解決することです
スレッドプールが使用されていない場合、システムが同じタイプのスレッドを多数作成し、メモリ不足または「オーバースイッチング」の問題を引き起こす可能性があります。
3. [必須]スレッドプールはエグゼキューターを使用して作成することはできませんが、ThreadPoolExecutorを介して作成されます。この
処理方法により、学生はスレッドプールの操作ルールをより明確に記述し、リソース枯渇のリスクを回避できます。

注:エグゼキューターによって返されるスレッドプールオブジェクトの欠点は次のとおりです。1)FixedThreadPoolおよびSingleThreadPool:
許容される要求キューの長さはInteger.MAX_VALUEであり、これにより多数の要求が蓄積され、OOMが発生する可能性があります。2)CachedThreadPool:
作成できるスレッドの数はInteger.MAX_VALUEです。これにより、多数のスレッドが作成され、OOMが発生する可能性があります。

 カスタムスレッドプールの利点

1.スレッドプール内のスレッドを再利用して、スレッドの頻繁な作成と破棄によって引き起こされるパフォーマンスの消費を回避します。

2.スレッドによるリソースの過度のプリエンプションによって引き起こされるシステムの輻輳を防ぐために、スレッドの最大同時数をより効果的に制御します。

3.スレッドを効果的に管理します。

非常に多くの利点があるので、カスタムスレッドプールThreadPoolExecutorについて説明しましょう。

ExecutorService exec = new ThreadPoolExecutor(20,
                            100,
                            60,
                            TimeUnit.SECONDS,
                            new ArrayBlockingQueue<Runnable>(4),
                            Executors.defaultThreadFactory(),
                            new ThreadPoolExecutor.CallerRunsPolicy());

1.パラメータの詳細な説明

上から下への7つのパラメータ

1.corePoolSize

コアスレッドの数。スレッドプールが作成された後、デフォルトではスレッドプールにスレッドはありません。代わりに、タスクの到着を待った後にタスクを実行するスレッドが作成されます。スレッドプール内のスレッド数がcorePoolSizeに達すると、新しいタスクはThreadPoolExecutor#prestartAllCoreThreads()メソッドまたはThreadPoolExecutor#prestartCoreThread()メソッドが呼び出されない限り、キャッシュキュー、つまりworkQueue追加されます(これら2つのメソッドの名前から、pre-を意味することがわかります。作成されたスレッド、つまり、タスクが到着しない前に、corePoolSizeスレッドまたは1つのスレッドを作成するだけです)。

PS:多くの人は、この数字をどれだけ記入すればよいかわかりません。実際、特に絡み合う必要はありません。実際の状況に応じて記入するだけです。わからない場合は、 Aliエンジニアの記述によると、次の値:
int NUMBER_OF_CORES = Runtime.getRuntime()。availableProcessors();

2.maximumPoolSize

スレッドプール内のスレッドの最大数。スレッドプールに作成できるスレッドの最大数を示します。多くの人は、その機能は次のように考えています。「スレッドプール内のタスクの数がcorePoolSizeを超えると、スレッドプールは次の数になるまでスレッドを作成し続けます。スレッドプール内のスレッドがmaximumPoolSize未満です」実際、この理解は完全に間違っています。その実際の役割は次のとおりです。スレッドプール内のスレッド数がcorePoolSizeおよびworkQueuefullに等しい場合、スレッド数がmaximumPoolSizeの前よりも多いかどうか、maximumPoolSizeで定義された値よりも少ない場合は、作成を継続するかどうかを確認する必要があります。タスクを実行するスレッド。それ以外の場合、適切なタスクの呼び出しは、このタスクを拒否するポリシーを拒否しますさらに、corePoolSizeを超えるスレッドは「アイドルスレッド」と呼ばれ、スレッドのこの部分には最大アイドルサバイバル時間(keepAliveTime)があります。このアイドルサバイバル時間の後にタスクが割り当てられない場合、スレッドのこの部分はリサイクルされます。

3.keepAliveTime

「アイドルスレッド」スレッドのアイドル生存時間を制御しますこのアイドルスレッドは、上記のcorePoolSizeの後に新しく作成されたスレッドです。デフォルトでは、このパラメーターは、スレッドプール内のスレッド数がcorePoolSizeより大きく、これらの「アイドルスレッド」にタスクが割り当てられていない場合にのみ開始されます。さらに、ThreadPoolExecutor#allowCoreThreadTimeOut(boolean)メソッドが呼び出された場合、スレッドプール内のスレッドの数はcorePoolSize以下であり、これらのコアスレッドにはタスクが割り当てられていないため、keepAliveTimeパラメーターも機能します。

4.ユニット

パラメータkeepAliveTimeの時間単位には、TimeUtilで定義されている7つの値があります。

TimeUnit.DAYS;              //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

5.workQueue

キューをブロックしています。現在のスレッドプール内のスレッド数> = corePoolSizeの場合、タスクが来るたびに、タスクはキューに追加されます。corePoolSizeがcorePoolSizeを超えている限り、タスクはキャッシュキューに追加されることに注意してください。 。追加は成功または失敗する可能性があります。成功した場合は、アイドル状態のスレッドがタスクを実行するのを待ちます。追加が失敗した場合(通常はキューがいっぱい)、現在のスレッドに従ってタスクを処理する方法を決定します。プールステータス(スレッド数がmaximumPoolSize未満の場合は、新しいスレッドを作成します。スレッド数がmaximumPoolSizeを超える場合は、拒否戦略に従って特定の処理が実行されます)。

一般的に使用されるブロッキングキューは次のとおりです。

1)ArrayBlockingQueue       //基于数组的先进先出队列,此队列创建时必须指定大小;
2)LinkedBlockingQueue      //基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
3)synchronousQueue        //这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

6.threadFactory

スレッドファクトリー。スレッドプールのスレッドを作成するために使用されます。スレッドファクトリを指定しない場合、スレッドプールはExecutors.defaultThreadFactory()デフォルトのスレッドファクトリ作成するために呼び出し、その後の作成のスレッド優先度はすべてThread.NORM_PRIORITYです。スレッドファクトリを指定すると、生成されたスレッドに対して特定の操作を実行できます。

7.ハンドラー

ポリシーの実施を拒否します。スレッドプールのキャッシュキューがいっぱいになり、スレッドプール内のスレッド数がmaximumPoolSizeに達すると、タスクが来る場合、タスク拒否戦略が採用されます。通常、次の4つの戦略があります。

ThreadPoolExecutor.AbortPolicy:         // 丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:       // 也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:    // 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:      // 由调用线程处理该任务

デフォルトの4つのタイプはあまり友好的ではありません。上記の拒否ポリシーに加えて、拒否ポリシーをカスタマイズすることもできます。

 ExecutorService pool = new ThreadPoolExecutor(// 自定义一个线程池

                1, // coreSize

                2, // maxSize

                60, // 60s

                TimeUnit.SECONDS, new ArrayBlockingQueue<>(3) // 有界队列,容量是3个

                , Executors.defaultThreadFactory()

                , new MyRejected()//自定义拒绝策略

        );

カスタムMyRejectedlクラスはRejectedExecutionHandlerを継承します 

public class MyRejected implements RejectedExecutionHandler {

 

    @Override

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

        MyTask task = (MyTask) r;

        System.out.println("报警信息:"+task.getTaskName()+" 被线程池拒绝,没有被执行");

        //可以往消息队列中间件里面放 可以发Email等等

    }

}

 

2.タスクを送信するためのThreadPoolExecutorの3つの方法

executorService.execute(Runnable); //戻り値なし
executorService.submit(Runnable); //戻り値
inVokeAny();ランダムな結果を返す、invokeAll(); //すべての結果を返す

詳細については、https//www.cnblogs.com/ldq2016/p/8072563.htmlを参照してください。


3.スレッドプールの終了

スレッドプールを使用する必要がない場合は、スレッドプールを閉じる必要があります...スレッドプールを閉じるには2つの方法があります...

i.shutdown()..。

  シャットダウンはスレッドプールを直接閉じませんが、新しいタスクを受け入れなくなります...スレッドプールにタスクがある場合、これらのタスクが実行された後、スレッドプールは閉じられます...

ii.shutdownNow()

  この方法は、新しいタスクが受け入れられず、タスクキュー内のタスクが直接削除されることを意味します。実行中の何かがある場合は、停止してみてください...

 

 

おすすめ

転載: blog.csdn.net/zhaofuqiangmycomm/article/details/113311879