一般的に、生産タスクの速度は消費速度よりも大きくなります。詳細の問題は、キューの長さと、生産と消費の速度に合わせる方法です。
典型的なプロデューサー - 消費者モデルは次のとおりです。
一般的な生産のために、消費よりも速いです。キューがいっぱいになった場合、この時点では、タスクを提出する方法についてより良いアプローチをブロックする前に、タスクを無視することはできません。 。また、ブロッキングは簡単に作成されます。
さらに、キューが空である場合、消費者はそれを取得する前にしばらく待つことができます。 。このようにして、プロデューサーが実際に生産を停止した場合、消費者は無限に待つことはありません。
したがって、ブロッキングをサポートする効率的な生産および消費モデルが実装されます。
JUCがスレッドプールの実装を支援してくれたので、なぜこの一連のものを使用する必要があるのはなぜですか? ExecutorServiceを直接使用する方が便利ではありませんか?
ThreadPoolexecutorの基本構造を見てみましょう。
しかし、問題は、ThreadPoolexecutorを構築するときにキューの実装としてブロックキューを手動で指定したとしても、実際にキューがいっぱいになった場合、executeメソッドがブロックされていないことです。
コードコピーは次のとおりです。
public void execute(runnableコマンド){
if(command == null)
新しいnullpointerexception();
if(poolsize> = corepoolsize ||!addifundercorepoolsize(command)){
if(runstate == running && workqueue.offer(command)){
if(runstate!= running || poolsize == 0)
suresqueuedtaskhandled(command);
}
else if(!addifundermaximumpoolsize(command))
拒否(コマンド);
}
}
現時点では、結果を達成するために何かをする必要があります。プロデューサーがタスクを提出し、キューがいっぱいになると、プロデューサーはそれをブロックして、タスクが消費されるのを待つことができます。
重要なのは、同時環境では、キューフルをプロデューサーによって審査できないことです。ThreadPoolexecutor.getQueue()。サイズ()を呼び出してキューがいっぱいかどうかを判断できないことです。
スレッドプールの実装では、キューがいっぱいになると、建設中に拒否された拒否Executionhandlerが渡され、タスクの処理を拒否します。デフォルトの実装はAbortPolicyであり、拒否ExecutionExceptionを直接スローします。
ここでは、この戦略が私たちのニーズに近づいているため、いくつかの拒否戦略について説明します。ユーザーが行った作業。プロデューサーはブロックされていませんが、提出タスクも停止されます。
コードコピーは次のとおりです。
public static class callerrunspolicyは拒否されたexecutionhandlerを実装しています{
/**
* a <tt> callerrunspolicy </tt>を作成します。
*/
public callerrunspolicy(){}
/**
*執行者がいない限り、発信者のスレッドでタスクrを実行します
*シャットダウンされています。その場合、タスクは破棄されます。
* @param r実行される実行可能なタスクの実行を要求しました
* @param eこのタスクを実行しようとする執行者
*/
public void relijectedexecution(runnable r、threadpoolexecutor e){
if(!e.isshutdown()){
r.run();
}
}
}
ただし、この戦略には、生産者が少ない場合、プロデューサーがタスクを消費している間、消費者はすべてのタスクを消費していますこのプロセスは、消費者のスレッドに飢えにつながる可能性があります。
同様のアイデアを参照する最も簡単な方法では、拒否ExexecutionHandlerを直接定義し、プロデューサーのブロッキングを実現するためにキューがいっぱいになったらBlockingQueue.putを呼び出すように変更できます。
コードコピーは次のとおりです。
new redjectedexecutionhandler(){
@オーバーライド
public void relijectedexecution(runnable r、threadpoolexecutor executor){
if(!executor.isshutdown()){
試す {
executor.getqueue()。put(r);
} catch(arternedexception e){
//中断しないでください
}
}
}
};
このようにして、キューと消費者のロジックを気にする必要はありません。
元の設計と比較して、この方法はコードの量を減らし、同時環境で多くの問題を回避できます。もちろん、送信時にセマフォを入力制限として使用するなど、他の手段を使用することもできますが、プロデューサーをブロックしたい場合は複雑に思えます。