序文
マルチスレッドプログラミングでは、各タスクにスレッドを割り当てることは非現実的であり、スレッド作成のオーバーヘッドとリソースの消費は非常に高いです。スレッドプールが生まれ、スレッドを管理するための強力なツールになりました。 Executorインターフェイスを通じて、Javaはタスク提出プロセスを切り離し、プロセスを実行する標準的な方法を提供し、実行可能に使用してタスクを表現します。
次に、JavaスレッドプールフレームワークThreadPoolexecutorの実装を分析しましょう。
次の分析は、JDK1.7に基づいています
ライフサイクル
ThreadPoolexecutorでは、上部3ビットの容量を使用して、実行状態を表すために使用されます。
1.ランニング:タスクキューで新しいタスクとプロセスタスクを受信する
2.Shutdown:新しいタスクを受け取らず、タスクキューを処理するタスク
3.ストップ:新しいタスクは受け取られておらず、タスクキューがリリースされず、進行中のすべてのタスクが同時に中断されます
4.統合:すべてのタスクが終了し、ワーカースレッドの数は0です。この状態に到達すると、終了()が実行されます。
5.終了:終了()が実行されました
状態遷移図
原子クラスは、threadpoolexecutorの状態ビットを表すために使用されます
プライベート最終AtomicInteger CTL = new AtomicInteger(ctlof(running、0));
スレッドプールモデル
コアパラメーター
corepoolsize:最小のリビングワーカースレッド数(approwcorethreadtimeoutが設定されている場合、この値は0です)
Maximumpoolsize:容量によって制限されたスレッドの最大数
KeepAlivetime:対応するスレッドのサバイバル時間、タイムユニットはTimeUnitによって指定されます
workqueue: work queue、実行するタスクを保存します
拒否ExecutionHandler:拒否ポリシー、スレッドプールの最大容量はスレッドプールがいっぱいになった後にトリガーされます。最初の3ビットはフラグビットとして使用されます。つまり、ワーカースレッドの最大容量は(2^29)-1です
4つのモデル
cachedthreadpool:キャッシュ可能なスレッドプール。スレッドプールの現在のサイズが処理要件を超えると、アイドルスレッドがリサイクルされます。需要が増加すると、新しいスレッドを追加できます。スレッドプールのサイズに制限はありません。
固定ThreadPool:固定サイズのスレッドプール。タスクを送信すると、スレッドプールの最大数に達するまでスレッドが作成されます。現時点では、スレッドプールのサイズは変更されなくなります。
SingleThreadPool:タスクを実行するためのワーカースレッドが1つしかないシングルスレッドスレッドプール。これにより、タスクがキューにある順序で連続的に実行されるようにすることができます。このスレッドが異常に終了する場合、タスクを実行するために新しいスレッドが作成されます。
ScheduledThreadPool:固定サイズのスレッドプールと、タイマーと同様に、遅延または時限でタスクを実行します。
タスクの実行を実行します
コアロジック:
1.現在のスレッド数<corepoolsize、新しいコアスレッドを直接開き、タスクを実行する(コマンド、true)
2。現在のスレッド数> = corepoolsize、およびタスクは作業キューに正常に追加されます
1)。スレッドプールの現在の状態が実行中かどうかを確認します
2)。そうでない場合、タスクは拒否されます
3)。その場合、スレッドの現在の数が0であるかどうかを判断します。0の場合は、ワーカースレッドを追加します。
3.通常のスレッド実行タスクをオンにして、ワーカー(コマンド、false)を追加し、起動に失敗した場合はタスクを拒否します。上記の分析から、スレッドプール操作の4つの段階を要約できます。
1).poolsize <corepoolsizeとキューは空です。提出されたタスクを処理するために、新しいスレッドが作成されます。
2).poolsize == corepoolsize。この時点で、提出されたタスクは作業キューに入ります。ワーカースレッドは、キューからタスクの実行を取得します。現時点では、キューは空ではなく、いっぱいでもありません。
3).poolsize == corepoolsizeとキューはいっぱいです。提出されたタスクを処理するために新しいスレッドも作成されますが、プールサイズ<maxpoolsize
4).poolsize == maxpoolsizeとキューがいっぱいになると、拒否ポリシーがトリガーされます
拒否ポリシー<br />先に述べました。タスクを実行できない場合、拒否されると述べました。拒否ExecutionHandlerは、拒否されたタスクを処理するためのインターフェイスです。ここに4つの拒否戦略があります。
AbortPolicy:デフォルトのポリシー、タスクの終了、拒否Exceptionをスローします
callerrunspolicy:例外をスローせずに発信者スレッドで現在のタスクを実行する
DisdardPolicy:ポリシーを破棄し、タスクを直接廃棄し、例外を投げないでください
Di Scandolderspolicy:最古のタスクを放棄し、現在のタスクを実行し、例外をスローしないでください
スレッドプールのワーカー
労働者は、Abstractueuedsynchronizerを継承し、実行可能です。前者はワーカーにロック機能を提供し、後者はワーカースレッドランワーカー(ワーカーW)(タスクキューからのスナップタスク実行)の主な方法を実行します。労働者の参照は労働者のコレクションにあり、メインロックによって保護されています。
プライベートファイナルREENTRANTLOCK MAINLOCK = new ReentrantLock();
プライベートファイナルハッシュセット<ワーカー> new Hashset <Worker>();
コア関数ランワーカー
以下は簡素化されたロジックです。注:各ワーカースレッドの実行は、次の機能を実行します
final void runworker(worker w){thread wt = thread.currentthread(); runnable task = w.firsttask; w.firsttask = null; while(task!= null ||(task = gettask())!= null){w.lock(); Exexecute(wt、task); task.run(); afterexecute(タスク、スロー); w.unlock(); } ProcessWorkEREXIT(W、完了);} 1。GetTask()からタスクを取得します
2。労働者をロックします
3。exexecute(wt、task)を実行します。これは、threadpoolexecutorがサブクラスに提供する拡張方法です
4。タスクを実行します。ワーカーが最初のタスクを構成した場合、最初のタスクは最初に1回だけ実行されます。
5。afterexecute(タスク、スロー)を実行します。
6。労働者のロックを解除します
7.取得したタスクがnullの場合、ワーカーを閉じます
タスクGetTaskを取得します
スレッドプール内のタスクキューは、建設中に渡されるブロッキングキューです。
プライベートファイナルブロッキングキュー<runnable> workqueue;
getTask()タスクキューからタスクを取得し、タスクを待つブロッキングとタイムアウトをサポートします。 4つの状況により、ヌルが返され、労働者が閉鎖されます。
1.既存のスレッドの数は、スレッドの最大数を超えています
2。スレッドプールは停止状態にあります
3.スレッドプールはシャットダウン状態にあり、作業キューは空です
4.スレッド待機タスクタイムアウト、およびスレッドの数が保持されたスレッドの数を超える
コアロジック:ブロッキングキューの待機タスクをタイムアウトまたはブロックします。タイミングで待機タスクにより、ワーカースレッドが閉じられます。
timed = aoplcorethreadtimeout || wc> corepoolsize; runnable r =タイミング? workqueue.poll(keepalivetime、timeunit.nanoseconds):workqueue.take();
2つのケースでタスクを待つことはタイムアウトします。
1.コアスレッドがタイムアウトを待つようにする、つまり、akolcorethreadtimeout(true)
2。現在のスレッドは通常のスレッドです。この時点でwc> corepoolsize
ワークキューはブロッキングキューを使用しているため、ここでは拡張しません。後で詳細な分析を書きます。
要約します
ThreadPoolexecutorは、プロデューサー - 消費者モデルに基づいています。タスクの送信の運用はプロデューサーと同等であり、実行タスクを実行するスレッドは消費者と同等です。
執行者は、ThreadPoolexecutorに基づいてスレッドプールモデルを構築する4つの方法を提供します。さらに、ThreadPoolexecutorを直接継承し、ExexecuteおよびAfter Exexecuteメソッドを書き換えて、Threadプールタスク実行プロセスをカスタマイズできます。
境界のあるキューまたは無制限のキューを使用すると、特定の状況に応じて考慮する必要があり、作業キューのサイズとスレッドの数も慎重に考慮する必要があります。
拒否ポリシーは、タスクを放棄したり例外を投げたりしないCallerrunSpolicyを使用するために推奨されますが、代わりに実行のためにタスクを発信者スレッドに戻します。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。