プロジェクト事業の急速な拡大に伴い、あなたが各モジュール内の空き個別のスレッドをたくさん気づいた、一度スレッドのモニタリングと最適化したかった、コードが戦争に行く必要があります。
私はあなたがrxjavaを使用確信している、okHttpこれらの人気のフレームワークは、彼らは内部スレッドのスケジューリングを伴い、そしてあなたがAPIのシリーズを使用するための良いパッケージ、あなたも、これらのスレッドが動作しているかを気にする必要はありません。何の問題だけでは、それらを使用していない場合は、しかし、あなたはそれは彼らが使用されているかの観点からプロジェクトのアーキテクチャを再考する必要があるかどうかを検討している場合。
なぜ、スレッドプールを使用できますか?
- スレッドは希少資源で、その作成には、システムリソースを大量に消費します。
- スレッドが頻繁に破壊され、GCは、頻繁にシステム性能をメカニズムをトリガーします。
- 複数のスレッドの同時実行が一元管理と監視の欠如します。
スレッドプールを使用します
それは、スレッドプールを作成する一般的な方法を提供し、エグゼキュータのクラス別スレッドプールJavaと契約が完了し作成します。
- newFixedThreadPool
- newSingleThreadExecutor
- newCachedThreadPool
- newScheduledThreadPool
背後にあるのは、例を見てみましょう、それらを導入していきます。
public void main() {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for(int i = 0; i < 20; i++) {
executorService.execute(new MyRunnable(i));
}
}
static class MyRunnable implements Runnable {
int id;
MyRunnable(int id) {
this.id = id;
}
@Override
public void run() {
try {
Thread.sleep(3000);
Log.i("threadpool", "task id:"+id+" is running threadInfo:"+Thread.currentThread().toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
例では、スレッドプールスレッドの固定数を作成し、これらのタスク20を追加します。
印刷は、プール-1スレッド2、スレッドプール-1-スレッド-3さんを3秒ごとに、すべてのタスクがプール-1-スレッド-1と命名されている3枚の印刷後にログを印刷し、ログを見ることができますスレッドプールの試合の大きさのために私たちを設定しており、実行されます。この現象の理由は、スレッドプールで唯一の3つのスレッド、20時間のタスクは最初の3つの重点課題を実行するために、スレッドプールに追加されたときに、タスクは背後待っています。
我々がもしExecutorService executorService = Executors.newFixedThreadPool(3);
するように変更ExecutorService executorService = Executors.newCachedThreadPool();
結果を見てください。
インスタントタスクの実行は、以上ですnewCachedThreadPoolの道を作成したスレッドプールを使用するように期待することができ、タスクを実行するなど、多くのスレッドを作成します。
次に、我々は説明するために、3つの部分に分け、スレッドプール内の公式作品を見てください。
一般的なスレッドプールの種は、既に上述したように、我々は彼らが外に作成されている方法を見て、私たちは2個の栗を与えます。
# -> Executor.newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
# -> Executor.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
复制代码
スレッドプールが行わThreadPoolExecutorを通して見える作成し、そのコンストラクタを見てください。
# -> ThreadPoolExecutor构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
复制代码
パラメータのコンストラクタ文のシリーズは、我々は彼らの特定の意味を見て、彼らはあなたが習得したスレッドプールの基本原則を理解することが非常に重要です。
- あなたがコアスレッドタイムアウト(allowCoreThreadTimeOut)を設定しない限り、スレッドのcorePoolSizeコア数は、スレッドがアイドル状態であっても、スレッドプールのスレッドで生きてきました。
- スレッドの最大数が存在することを可能にするmaximumPoolSizeスレッドプール。
- ワークキューのワークキュー、コアスレッドがビジー状態であるとき、ジョブキューにジョブ投入。作業キューも容量を超えた場合、それは非コアタスクを実行するスレッドを作成しようとすることです。
- keepAliveTimeが非コアスレッドは、ほとんどの時間アイドル状態のスレッドの値は、次にリサイクルさ上回ります。
- スレッドを作成するためのthreadFactoryスレッドファクトリクラス。
- な廃棄などのRejectedExecutionHandler作業キュー飽和戦術は、その上の例外をスローします。
スレッドプールが作成された後、実行方法は、タスクによって、現在の動作状況や特定のパラメータに応じて以下の全体的なモデルをスレッドプール処理を供給するタスクを提出することができます。
明確に下の図は、タスクの後にスレッドプールの提出プロセスを示し、それらを繰り返しません。
我々はいくつかの一般的なスレッドプールの使用中のエグゼキュー・ユーティリティ・クラスの構造パラメータを見て次のようなものです。
スレッドプールの種類 | スレッドのコア数 | スレッドの最大数 | 非コアスレッドアイドル時間 | 作業キュー |
---|---|---|---|---|
newFixedThreadPool | 特定 | 特定 | 0 | LinkedBlockingQueue |
newSingleThreadExecutor | 1 | 1 | 0 | LinkedBlockingQueue |
newCachedThreadPool | 0 | Integer.MAX_VALUEの | 60年代 | SynchronousQueue |
newScheduledThreadPool | 特定 | Integer.MAX_VALUEの | 0 | DelayedWorkQueue |
前記特定のユーザに固定値を渡す必要のことをいいます。
ここでは、追加の分析を阻止するためにキューイングする必要があります。
ブロッキングキュー
ノンブロッキングでキューをブロック使用はそれをしないだろう、なぜあなたは今まで疑問に思っていますか?
一般的に生産に使用されるキューを遮断する事実 - 消費者モデルを使用して、非ブロックキューを使用する場合、彼らは、別のスレッドで通常は、プロデューサーのタスクは、消費者のタスクを実行するようにスケジュールされ、追加され、それは必然的に追加の処理同期戦略が必要になりますそして、スレッド間には、ポリシーを覚まします。タスクキューが空の場合、新しいタスクが消費者スレッド処理タスクを覚ますためにキューに追加された要素を、摂取した場合、例えば、消費者のスレッドがブロックされています。
ブロッキングキューは要素とアクセス要素を追加する際にロック操作(ロック+条件)の多様性を達成するために設定されています。
処理フローチャートスレッドプールによれば、キューサイズの遮断が直接非コアスレッドを作成する能力に影響を与えるため、別の懸念は、キューを遮断容量の問題です。具体的には、キューがいっぱいで、非コアスレッドの障害物を作成しませんが、タスクが(もしあれば)を実行するために、ブロッキングコアスレッドの後ろに待機しているキューに追加していきます。
- 内部ブロック実装LinkedBlockingQueueは、デフォルトのコンストラクタを使用して、リストキューをリンク
Integer.MAX_VALUE
容量と、それは多くの場合、「無制限」、と言われ、もう一方は、コンストラクタと容量パラメータの容量によって制限される場合があります。スレッドプールのサイズを作成したエグゼキュータ・ユーティリティ・クラスを使用すると、無制限です。 - SynchronousQueue容量が当面の課題は、それは、それぞれの除去作業を伴う挿入操作の必要があり、その逆で、消費に追加トリガするがあるたびに、0です。
- DelayedWorkQueueアレイ、16のデフォルトの容量を達成するため、動的な拡張をサポートするには、タスクはプライオリティキューと同様の遅延を、分類することができ、タイミングや遅延のScheduledThreadPoolExecutorタスクに達成することができます。
- それはアレイ実装、固定することができない容量の拡張に基づいているスレッドプールシステム、されていない上にArrayBlockingQueue。
あなたは、実際の需要に基づいて、適切なブロッキングキューを選択する必要があり、そして今、私たちは、これらのスレッドプールの使用シナリオを見てください。
- これは、タスクがあまりさえアイドルタスクもスレッドのコア数残っていても、新しいスレッドを作成されないことを意味newFixedThreadPoolなし非コアスレッド、ことを特徴としています。タスクが大きいシーンではないながらキュー無制限、パフォーマンスを待って、長期的なタスクを実行するために比較的安定して最適です。
- タスクが実行順序のシーンのために必要となるようnewSingleThreadExecutor newFixedThreadPool 1は、スレッド1の数以来、スレッドの数に相当します。
- newCachedThreadPoolそれは非常にリソースを消費していないコアスレッドによって特徴付けられる、無制限の非中核スレッドが短時間でタスクを無制限に扱うことができますが、実際にスレッドを作成することで、あまりにも多くのスレッドがOOMに非常に可能性の高いリードを作成し、スレッドのタイムアウトを設定するには、また、それはもはや大規模並列不安定内のタスクの数、並列にフォローアップタスクの少量と追加のタスクシーンを実行する必要が使用することはできませんスレッドリソースの解放に関するものです。
- newScheduledThreadPoolは一般タイミングまたは遅延タスクに使用します。
実際の開発プロセスでは、タスクの規模、応答時間はおおよそ手動で決定された場合、提供直接使用エグゼキュータは、実際の需要の多様を作成するためにコンストラクタをThreadPoolExecutorなければならないために推奨するだけでなく、スレッドの数を制御すること自由に、タイムアウト、キューをブロックし、飽和されていません戦略は、(デフォルトでは、例外をスローしてAbortPolicy飽和戦略です)。
彩度戦略
4を、以下の内蔵飽和戦略
- DiscardPolicyは拒否されたタスクを破棄します。
- キューのDiscardOldestPolicyヘッドは、タスク、すなわち、最初のチームのタスクチームが部屋を作るためにアウトになり破棄されます。
- AbortPolicy RejectedExecutionExceptionは例外をスローします。
- CallerRunsPolicyは、executeメソッドの呼び出し元のスレッドに拒否されたタスクを実行します。
また、ユーザーは、インターフェイスを通過することは、コンストラクタ引数でカスタムのRejectedExecutionHandler飽和ThreadPoolExecutor複数のポリシーを実装することがあります。
次は、スレッドプールの継承構造を見てする必要があります。
スレッドプールのクラス図
- 我々はタスクを送信する際に使用される方法である、唯一の方法は、実行定義エグゼキュータベースクラスインターフェースは、戻り値を持たない追加タスクを実行します。
- ExecutorServiceのは、まだ概念をプールし始め、メソッドが戻り結果でジョブを提出し、スレッドプールのシャットダウン方法をシャットダウン提出を定義し、インターフェイスです。
- AbstractExecutorServiceインターフェース方法は、残りのシャットダウンのほとんどを実装し、関連する抽象メソッドが実装されていない実行します。
- 最も一般的なスレッドプールのThreadPoolExecutor
- ScheduledThreadPoolExecutorのは、スレッドプールの遅延をサポートするための一連のタスクを定義します。
- ForkJoinPoolと複数のサブタスクに細分化タスクの分割統治アイデアを使用ThreadPoolExecutorは、複数のスレッドで実行するさまざまな問題を解決します。例えば、1-1000000整数を計算すると、これらのプログラムのThreadPoolExecutorに応じて複数にタスク分割がスレッドプールに送信され、そしてForkJoinPool溶液によれば、スレッドプールに詳細作業分割のタスクをジョブを送信することですカスタムタスクの実行に言及しました。小さなパートナーは、関心のある記事を参照して説明ForkJoinPoolマルチスレッド。
選択したスレッドプールサイズ
我々は、スレッドプール、それのサイズを選択する必要がありますどのように実際の戦闘では、スレッドプールの内部構造を理解できますか?
これは、タスクの一般的な理解は、CPU集約型またはIO集約型である必要があり。
- たとえば、CPU集約型のコンピューティングタスク、高いCPU使用率、多数の、それが原因で性能低下が頻繁にCPUのスレッドのスケジューリング結果を行うためにスレッドを開く場合は、この時点でのようにします。スレッドのCPUコアの数+1、プラス1のための一般的な推奨事項は、スレッドがブロックされている候補者や計画外停止などのコアを防ぐためです。
- 一般的にはIO集約的なファイルI / O、ネットワークI / Oなどを指します。比IO時間がかかり、に関連する時間のかかるCPUスレッドの数を選択
最佳线程数 = CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]
I / Oデバイスの比率とCPU使用率を設定した理由が最大化されています。
処理された単核、CPUコンピューティングおよびI / O操作は1:2、例えば、3つのスレッドが(からこの場合、100%のCPU使用率まで見ることができる--Java実際の並行プログラミング非常に時間オフ)。
スレッドプールの状態
スレッドプールの状態は、キューに追加タスクを実行するときに、スレッド・プールかどうかを決定することで、その後、スレッドプールがシャットダウンされている場合は、この時点で、動作状態を判断し、タスクを除去するであろうようなタスクを追加するなど、全体のプロセスのタスクに不可欠です飽和戦略を実行します。
私たちは、次のいくつかの州のスレッドプールに回します:
- RUNNING:タスクは新しい提出を受け入れることができ、かつ障害物のタスクキューを処理することができます。
- SHUTDOWN:新しい提出タスクを受け入れるもはや、状態をシャットダウンしていないが、それはブロッキングキューに保存されたジョブを処理し続けることができます。スレッドプールは、RUNNING状態にある場合には、シャットダウンを呼び出します()メソッドは、スレッドプールの状態を入射させます。(ファイナライズ()メソッドは、()メソッドは、状態に移行シャットダウンの実行中に呼び出されます)。
- STOP:新しいタスクを受け入れることができない、タスクキュー、中断されたスレッドが処理しているタスクを処理しません。スレッドプールは、実行中またはシャットダウン状態にあるときに、呼び出しshutdownNowの()メソッドは、スレッドプールの状態を入力させます。
- 片付け:すべてのタスクが終了している場合は、workerCount(スレッドの有効数)が0、TERMINATED状態に入るための状態コール終了()メソッドを入力した後、スレッドプールです。
- TERMINATED:この状態が終了()メソッドの実装後に入力され、終了デフォルト()メソッドは何もしません。
状態を示すフロー図
概要
戻る記事の先頭への質問:RxJavaとOkHttpそれをスケジュールするスレッドプールを使用していますか?
- Rxjava内部スレッドスケジューラは、一般的にいくつかの使用定義
Schedulers.io()
とSchedulers.computation()
内部の両方使用して、CPU集約及び集約IOスケジューラに対応するScheduledThreadPoolExecutor
スレッドプールを、それがコール遅延チェーンの遅延操作等が可能ですそして、設計上の考慮事項。違いは、スレッドの最大数はioをしながら、コアCPUの数とスレッドの最大数の計算のスレッドの二つの異なる最大数は、無限であることです。 - デフォルトのスレッドプールはOkHttpにある
newCachedThreadPool
高い並行性の考慮に起因すると選択を行うことができる。このスレッドプールの欠点の上に導入され、それは柔軟実際の使用における実際の状況に応じて設定することができます。
# -> Dispatcher
public Dispatcher() {
}
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
复制代码