2。はじめに
マルチスレッドテクノロジーは、主にプロセッサユニットでの複数のスレッド実行の問題を解決します。プロセッサユニットのアイドル時間を大幅に削減し、プロセッサユニットのスループット機能を高めることができます。ただし、頻繁な糸の作成のオーバーヘッドは非常に大きいです。したがって、オーバーヘッドのこの部分を減らす方法は、スレッドプールの使用を検討する必要があります。スレッドプールはスレッドコンテナであり、一度に定格のスレッド数のみを実行します。スレッドプールは、これらの定格のスレッド数を管理するために使用されます。
3。スレッドプールを含むクラス構造図
その中でも、私たちが使用する主なものはThreadPoolexecutorクラスです。
4.スレッドプールの作成方法
通常、スレッドプールを作成するための次の方法があります。
1.エグゼキューターファクトリークラスを使用します
執行者は主に、スレッドプールを作成するための次の方法を提供します。
以下の使用例を見てみましょう。
1)newFixedThreadPool(固定スレッドプール)
public class sixtthreadpool {public static void main(string [] args){executorservice pool = executors.newfixedthreadpool(5); //固定サイズは5の固定サイズ(int i = 0; i <10; i ++){pool.submit(new mythread()); } pool.shutdown(); }} public class mythread extends thread {@override public void run(){system.out.println(thread.currentthread()。getname() + "execution ..."); }}テスト結果は次のとおりです。
pool-1-thread-1が実行されています。 。 。
Pool-1-Thread-2が実行されています。 。 。
Pool-1-Thread-3が実行されています。 。 。
Pool-1-Thread-2が実行されています。 。 。
Pool-1-Thread-3が実行されています。 。 。
Pool-1-Thread-2が実行されています。 。 。
Pool-1-Thread-2が実行されています。 。 。
Pool-1-Thread-3が実行されています。 。 。
Pool-1-Thread-5が実行されています。 。 。
Pool-1-Thread-4が実行されています。 。 。
固定サイズのスレッドプール:スレッドがスレッドプールの最大サイズに達するまで、タスクが送信されるたびにスレッドを作成します。スレッドプールのサイズは、最大値に達すると変更されません。実行例外のためにスレッドが終了すると、スレッドプールは新しいスレッドを追加します。
2)newsinglethreadexecutor(シングルスレッドプール)
public class singlethreadpool {public static void main(string [] args){executorservice pool = executors.newsinglethreadexecutor(); //(int i = 0; i <100; i ++){pool.submit(new mythread()); } pool.shutdown(); }}テスト結果は次のとおりです。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
シングルスレッドスレッドプール:このスレッドプールには、1つのスレッドが機能するのは1つだけです。つまり、すべてのタスクのシングルスレッドシリアル実行が行われます。この一意のスレッドが例外のために終了する場合、それを置き換える新しいスレッドがあります。このスレッドプールにより、すべてのタスクの実行順序がタスク提出の順序で実行されることが保証されます。
3)NewsCheduledThreadPool
public class scheduledthreadpool {public static void main(string [] args){scheduledexecutorservice pool = executors.newscheduledthreadpool(6); for(int i = 0; i <10000; i ++){pool.submit(new mythread()); } pool.schedule(new mythread()、1000、timeunit.milliseconds); pool.schedule(new mythread()、1000、timeunit.milliseconds); pool.shutdown(); }}テスト結果は次のとおりです。
pool-1-thread-1が実行されています。 。 。
Pool-1-Thread-6が実行されています。 。 。
Pool-1-Thread-5が実行されています。 。 。
Pool-1-Thread-4が実行されています。 。 。
Pool-1-Thread-2が実行されています。 。 。
Pool-1-Thread-3が実行されています。 。 。
Pool-1-Thread-4が実行されています。 。 。
Pool-1-Thread-5が実行されています。 。 。
Pool-1-Thread-6が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………….. …………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………..
Pool-1-Thread-4が実行されています。 。 。
pool-1-thread-1が実行されています。 。 。
テスト結果の最後の2つのスレッドは、1秒を遅らせた後にのみ実行を開始します。このスレッドプールは、タイミングとタスクの定期的な実行の要件をサポートしています
4)NewCachedThreadPool(キャッシュ可能なスレッドプール)
public class cachedthreadpool {public static void main(string [] args){executorservice pool = executors.newcachedthreadpool(); for(int i = 0; i <100; i ++){pool.submit(new mythread()); } pool.shutdown(); }}テスト結果は次のとおりです。
Pool-1-Thread-5が実行されています。 。 。
Pool-1-Thread-7が実行されています。 。 。
Pool-1-Thread-5が実行されています。 。 。
Pool-1-Thread-16は実行中です。 。 。
Pool-1-Thread-17が実行されています。 。 。
Pool-1-Thread-16は実行中です。 。 。
Pool-1-Thread-5が実行されています。 。 。
Pool-1-Thread-7が実行されています。 。 。
Pool-1-Thread-16は実行中です。 。 。
Pool-1-Thread-18が実行されています。 。 。
Pool-1-Thread-10が実行されています。 。 。
キャッシュ可能なスレッドプール:スレッドプールのサイズがタスクの処理に必要なスレッドを超えると、一部のアイドルスレッド(60秒でタスク実行なし)がリサイクルされます。タスクの数が増えると、このスレッドプールは、タスクを処理するために新しいスレッドをインテリジェントに追加できます。このスレッドプールは、スレッドプールサイズを制限しません。これは、オペレーティングシステム(またはJVM)が作成できる最大スレッドサイズに完全に依存します。
当局者は、プログラマーがより便利なエグゼクティブファクトリーメソッドエグゼクティターを使用することを示唆しています。NewCachedThreadPool()(自動スレッドリサイクルを実行できる未結合のスレッドプール)、executors.newfixedThreadpool(int)(固定サイズのスレッドプール)executors.newsinglethreadexecutor()これらのスレッドプールは、ほとんどの使用シナリオのデフォルトで事前に定義されています。
2。threadPoolExecutorクラスを継承し、親クラスのコンストラクターメソッドをコピーします。
この方法を導入する前に、スレッドプールを作成するための以前のいくつかの基礎コードを分析しましょう。
public class executors {public static executorservice newfixedthreadpool(int nthreads){return new SthreadPoolexecutor(nthreads、nthreads、0l、timeunit.milliseconds、new linkedblockingqueue <runnable>()); } public static executorservice newsingLethReadExecutor(){return new finalizableDelegatedExecutorService(1、1、0l、timeUnit.milliseConds、new linkedblockingqueue <runnable>()); }}エグゼキューターファクトリークラスの基礎となるコードから、工場クラスが提供する方法がスレッドプールを作成する方法が実際に実装されていることがわかります。 ThreadPoolexecutorコンストラクターメソッドコードは次のとおりです。
Public SthreadPoolexecutor(int CorePoolsize、int maximumpoolsize、long keepalivetime、timeUnit unit、blockingqueue <runnable> workqueue、threadefactory threadfactory、rexededexecutionhandler Handler){if(corepoolsize <0 || maximumpoolsize <= 0 || maximumsizizezizeizizeizezizezizezizeize IllegalArgumentException(); if(workqueue == null || threadfactory == null || handler == null)新しいnullpointerexception(); this.corepoolsize = corepoolsize; this.maximumpoolsize = maximumpoolsize; this.workqueue = workqueue; this.keepalivetime = unit.tonanos(keepalivetime); this.threadfactory = threadFactory; this.handler = handler; }次に、ThreadPoolexecutorコンストラクターメソッドについて話しましょう。この構造方法には、主に次のパラメーターがあります。
corepoolsize-フリースレッドを含むプールで保存されているスレッドの数。
Maximumpoolsize - プールで許可されるスレッドの最大数。
KeepAlivetime-スレッドの数がCorePoolsizeよりも大きい場合、これはアイドルスレッドが新しいタスクを待つ最も長い時間です。
ユニット - キープリベティタイムパラメーター時間単位。
workqueue-実行前にタスクを維持するために使用されるキュー。このキューは、実行可能なメソッドによって送信された実行可能なタスクのみを維持します。
ThreadFactory-新しいスレッドを作成するために執行者が使用する工場。
ハンドラー - スレッドスコープとキュー容量のために実行時に使用されるハンドラー。
次に、これらのパラメーター間の関係について話しましょう。スレッドプールが作成されたばかりの場合、スレッドプールにスレッドはありません(スレッドプールが作成されるとすぐに、特定の数のスレッドが作成されているわけではないことに注意してください)。タスクを追加するためにexecute()メソッドが呼び出されると、スレッドプールは次の判断を下します。
1)現在実行されているスレッドの数がCorePoolsizeよりも少ない場合は、すぐに新しいスレッドを作成してこのタスクを実行します。
2)現在実行されているスレッドの数がCorePoolsize以上の場合、このタスクはキューに配置されます。
3)スレッドプールキューがいっぱいであるが、実行中のスレッドの数がMaximumumpoolsizeよりも少ない場合、このタスクを実行するために新しいスレッドが作成されます。
4)キューがいっぱいで、現在実行されているスレッドの数がMaximumpoolsize以上である場合、スレッドプールは拒否ポリシーに基づいて現在のタスクを処理します。
5)タスクが実行されると、スレッドはキューから次のタスクを実行して実行します。キューで実行するタスクがない場合、スレッドはアイドル状態になります。 KeepAliveTimeの生存時間が超えると、スレッドがスレッドプールによってリサイクルされます(注:リサイクルスレッドは条件付きです。現在実行されているスレッドの数がCorePoolsizeよりも大きい場合、スレッドが破壊されます。コアプールサイズよりも大きくない場合、スレッドは破壊されません。スレッドがアイドル状態になったらすぐにリサイクルされているのではなく、スレッドがリサイクルされる前にkeepalivetimeを超えるまで待つ必要があるのはなぜですか?その理由は非常に単純です。なぜなら、スレッドの創造と破壊は多くを消費し、頻繁に作成して破壊することはできないからです。 KeepAlivetimeを超えた後、このスレッドは実際には使用されておらず、破壊されることがわかります。この場合、ユニットはKeepAlivetimeの時間単位を表し、ユニットの定義は次のとおりです。
public enum timeUnit {nanoseconds {// keepAlivetime in nanoconds}、マイクロ秒{//マイクロ秒のkeepAlivetime}、milliseconds {// millisecondsでkeepalivetime}、秒{// keepaliveTime}、empily {// keepalivetime in hours}、days {// keepalivetime in days};以下のソースコードを分析しましょう。上記の状況では、主に関連するソースコードは次のとおりです。
Private Boolean AddifunderCorePoolsize(Runnable FirstTask){スレッドT = null;最終ReentrantLock MainLock = this.mainlock; MainLock.Lock(); try {if(poolsize <corepoolsize && runstate == running)t = addthread(firstTask); }最後に{mainlock.unlock(); } if(t == null)falseを返します。 t.start(); trueを返します。 }実際、このコードは非常に簡単です。主に、現在のスレッドプールがCorePoolsizeよりも小さい場合、タスクを処理するために新しいスレッドが作成されることを説明しています。
private boolean addifundermaximumpoolsize(runnable firsttask){thread t = null;最終ReentrantLock MainLock = this.mainlock; MainLock.Lock(); try {if(poolsize <maximumpoolsize && runstate == running)t = addThread(firstTask); }最後に{mainlock.unlock(); } if(t == null)falseを返します。 t.start(); trueを返します。 }上記のコードは、現在のスレッドプールの数がMaximumumpoolsizeよりも少ない場合、タスクを実行するためにスレッドが作成されることを説明しています。
5。スレッドプールのキュー
スレッドプールキューには3種類あります。
直接コミット:作業キューのデフォルトオプションはSynchronousqueueです。これは、タスクを維持せずにスレッドに直接送信します。ここで、タスクをすぐに実行するために利用できるスレッドがない場合、タスクをキューにしようとすると失敗し、新しいスレッドが構築されます。このポリシーは、内部依存関係を持つ可能性のあるリクエストセットを処理するときにロックを回避します。通常、直接の提出には、新しく提出されたタスクの拒否を避けるために、無制限のMaximumMumpoolsizeが必要です。この戦略により、キューが処理できる平均でコマンドが継続的に到着すると、無制限のスレッドが成長の可能性を持つことができます。
無制限のキュー:無制限のキュー(例えば、事前定義された容量のないLinkedBlockingQueue)を使用すると、すべてのcorepoolsizeスレッドがビジーである場合、キューで新しいタスクが待機します。このようにして、作成されたスレッドはCorePoolsizeを超えません。 (Maximumpoolsizeの価値は無効です。)各タスクが他のタスクとは完全に独立している場合、つまり、タスクの実行が互いに影響を与えない場合、固定されていないキューに適しています。たとえば、Webページサーバーで。このキューイングは、過渡バーストリクエストを処理するために使用できます。この戦略により、コマンドが継続的に到着した場合、キューが処理できる平均を継続的に超えて到着すると、未結合のスレッドが成長の可能性を持つことができます。
Bounded Queue:限られたMaximumpoolsizeを使用する場合、境界線(ArrayblockingQueueなど)を使用すると、リソースの疲労を防ぐのに役立ちますが、調整と制御が難しい場合があります。キューのサイズと最大プールサイズは相互にトレードオフする必要がある場合があります。大きなキューと小さなプールを使用すると、CPUの使用、オペレーティングシステムのリソース、コンテキストのオーバーヘッドを最小限に抑えることができますが、スループットが手動で削減される可能性があります。タスクが頻繁にブロックされている場合(たとえば、I/O境界の場合)、システムは許可するよりも多くのスレッドの時間をスケジュールする場合があります。通常、小さなキューを使用するには、プールサイズが大きくなり、CPUの使用量が高くなりますが、容認できないスケジューリングオーバーヘッドに遭遇する可能性があり、スループットを減らすこともできます。
以下のスレッドプールキューについて話しましょう。クラス構造図は次のとおりです。
1)同期
キューは、上記の直接提出に対応しています。まず第一に、Synchronousqueueはバウンドされていません。つまり、数値を保存する能力は無制限です。ただし、キュー自体の特性により、要素を追加した後、他のスレッドがそれらを奪うのを待つ必要があります。
2)LinkedBlockingQueue
キューは、上記の無制限のキューに対応しています。
3)ArrayBlockingQueue
キューは、上の境界キューに対応しています。 ArrayBlockingQueueには、次の3つのコンストラクターがあります。
public arrayblockingQueue(int capacity){this(capisome、false); } public arrayblockingqueue(int capational、boolean fair){if(caperation <= 0)throw new lilegalargumentexception(); this.items =(e [])new object [capuation]; lock = new ReentrantLock(FAIR); notempty = lock.newcondition(); notfull = lock.newcondition(); } public arrayblockingqueue(int capational、boolean fair、collection <?extends e> c){this(caperage、fair); if(caperation <c.size())は新しいIllegalargumentException()を投げます。 for(iterator <?extends e> it = c.iterator(); it.hasnext();)add(it.next()); }このフェアに焦点を当てましょう。フェアは、キューアクセススレッドの競争戦略を表します。 Trueの場合、タスク挿入キューはFIFOルールに準拠しています。 falseの場合、「キューをカットする」ことができます。たとえば、現在多くのタスクがキューイングしている場合、スレッドがタスクを完了し、新しいタスクが登場します。それが偽の場合、このタスクはキューでキューに掲載する必要はありません。キューを直接カットしてから実行できます。下の図に示すように:
6.スレッドプール拒否実行戦略
スレッドの数が最大値に達すると、この時点でタスクがまだ来ており、現時点ではタスクの受け入れを拒否しなければなりません。
ThreadPoolexecutorでは、タスクを追加するときに実行ポリシーのカスタマイズが失敗します。スレッドプールのsetRejectedexecutionhandler()メソッドを呼び出して、既存のポリシーをカスタマイズされた拒否Executionhandlerオブジェクトに置き換えることができます。 ThreadPoolexecutorが提供するデフォルトの処理戦略は、例外情報を直接破棄して同時に投げることです。 ThreadPoolexecutorは、4つの既存のポリシーを提供します。
threadPoolexecutor.abortPolicy:タスクが拒否され、例外がスローされていることを示します。ソースコードは次のとおりです。
public static class abortpolicyは、拒否Executionhandler { /*** abortpolicy < /tt>を作成します。 * / public abortpolicy(){} / ***は常に拒否されたexecutionexceptionをスローします。 * @param r実行可能なタスクの実行を要求します * @param eこのタスクを実行しようとする執行者 * @throwsは常に拒否されます。 */ public void reljectedexecution(runnable r、threadpoolexecutor e){throw new reljectedexecutionexception(); //例外スロー}}shoodpoolexecutor.discardpolicy:タスクが拒否されたが、アクションは行われないことを意味します。ソースコードは次のとおりです。
public static class discardpolicyは、拒否Executionhandler { /*** a <tt> docardpolicy < /tt>を作成します。 * / public discardPolicy(){} / ***は何もしません。 * @param r実行可能なタスクの実行を要求した * @param eこのタスクを実行しようとする執行者 */ public void reljectedexecution(runnable r、threadpoolexecutor e){} //直接拒否しますが、何もしません}StreadPoolexecutor.CallerrunSpolicy:タスクが拒否され、タスクが発信者のスレッドで直接実行されることを示します。ソースコードは次のとおりです。
public static class callerrunspolicyは、拒否されたexecutionhandler { /*** a <tt> callerrunspolicy < /tt>を作成します。 * / public CallerrunSpolicy(){} / ** *は、執行者 *がシャットダウンされていない限り、発信者のスレッドでタスクrを実行します。その場合、タスクは破棄されます。 * @param r実行可能なタスクの実行を要求 * * @param eこのタスクを実行しようとする執行者 */ public void reljectedexecution(runnable r、threadpoolexecutor e){if(!e.isshutdown()){r.run(); //タスクを直接実行}}}SthreadPoolexecutor.DiscardOldESTPolicy:タスクキューの最初のタスクが最初に破棄され、次にタスクがキューに追加されることを意味します。ソースコードは次のとおりです。
public static class didardoldoldestpolicyは、拒否されたexecutionhandler { /*** a <tt> discardoldestpolicy < /tt>を作成します。 */ public disdardoldestpolicy(){} public void reljectedexecution(runnable r、threadpoolexecutor e){if(!e.isshutdown()){e.getqueue()。poll(); //キューE.Execute(r)の最初のタスクを破棄します。 //新しいタスクを実行する}}}タスクが継続的に到着すると、タスクがキューからポーリングされ、新しいタスクが実行されます。
要約します
上記は、編集者が紹介したJDK独自のスレッドプールの詳細な説明です。私はそれが誰にでも役立つことを願っています。ご質問がある場合は、メッセージを残してください。編集者は、すべての人に時間内に返信します。 wulin.comのウェブサイトへのご支援ありがとうございます!