Java 開発としてのスレッド プールの使用は避けられないハードルです. スレッド プールを正しく使用する方法は、すべての開発が直面する問題です. 今日は、オープン ソース プロジェクトからトップ オープン ソース プロジェクトでスレッド プールがどのように使用されているかを見ていきます. . 私が最近参加したオープン ソース プロジェクトである RocketMQ の例を取り上げ、仕事で遭遇したいくつかの悪い習慣を組み合わせて、スレッド プールの使用について説明しましょう。次の側面から:
1.スレッドプールの作成
まずはRocketMQのスレッド作成BrokerController#initializeResources
方法。以下に示すように:
一見、カスタムスレッドプールのように見えますが、ソースコードを見ると、BrokerFixedThreadPoolExecutor
実際にThreadPoolExecutor
ますThreadPoolExecutor
。上記のスレッド プールの作成を分析すると、次の特徴が見つかります。
-
スレッドプールは名前で設定されます (推奨設定)
なぜ名前を設定する必要があるのですか? 主な理由は、問題をトラブルシューティングするときに、どのスレッド プールによって実行されたどのコード フラグメントに問題があるかを知ることです。ThreadPoolExecutor のデフォルト名を使用するように名前を設定すると、トラブルシューティング時にどのスレッド プールかわかりません。
-
スレッド プール内のスレッド数を設定する
ここで、このスレッドプールのスレッドを拡張する必要があるかどうかは、コアスレッドの数と最大スレッド数によって判断されます.たとえば、コアスレッドの数が10で、スレッドプールの最大数は10です. 100. その後、スレッドプールは動作中にコアスレッドの数を超えた後も継続します. タスクの実行を満たすためにスレッドを作成します. 一般的に、RocketMQ のコア スレッド プールと最大スレッド プールは同じサイズ、つまり固定サイズのスレッド プールに設定されていることがわかります。
-
タスク キューの設定容量をカスタマイズする (推奨設定)
多くは RocketMQ
LinkedBlockingQueue
で使用され、キューの容量を設定します。重要: キューの選択は個々のニーズに基づいていますが、キューに適切な容量を設定してください。容量を設定しない場合、デフォルトの容量は ですInteger.MAX_VALUE
。これにより、タスク拒否ポリシーをトリガーする前に、メモリ不足によるサービスのダウンタイムが大幅に発生する可能性があります。 -
タスク拒否ポリシーの選択
タスク拒否戦略の選択 一般に、JDK ThreadPoolExecutor のデフォルトの戦略を使用できますが、特別な要件がある場合、ユーザーは戦略をカスタマイズできます。
上記は、RocketMQ オープン ソース プロジェクトから見たスレッド プールの作成です。実際、多くの人はExecutors
create。詳細については、記事「スレッド プール分析の作成に Executors を使用することが推奨されない理由」を参照してください。著者は、この記事で分析を行います。
2.スレッドプールの使用
RocketMQ のソース コードを調べた人は、RocketMQ に多数のスレッド プールがあることに気付くでしょう。多くの人は、すべてのタスクを完了するために 1 つのスレッド プールを使用しないのはなぜだろうかと疑問に思うでしょう。著者の仕事は、主に次の理由によるものです。
2.1 統一の原則
統一の原則を理解するには?つまり、スレッド プールはタスク処理のクラスを実行する必要があります。BrokerController#initializeResources
のコードから、さまざまなタスクがさまざまなスレッド プールを使用して処理されていることがわかります。これを行う利点:
- スレッド実行問題のトレースバックは便利であるが、スレッドプールの実行でエラーが発生した場合、エラースタックに従って単一性をトレースバックしやすくする必要がある。すべてのタスクがスレッド プールを使用して実行されている場合、スレッド プール名ではどのタスクに問題があるかわかりません。
- キューのサイズをより適切に評価する機能
- スレッド プールの実行中に問題が発生した場合、スレッド プールのシャットダウンまたはその他の問題は、1 つのタイプのタスクのみに影響し、すべてのタスクには影響しません。
2.2 適切な梱包
スレッド プールを適切にカプセル化すると、セキュリティを満たしながらコードをより洗練されたものにすることができます。これが上記のセマンティクスです。
3. スレッドプールの不適切な使用
筆者の前回の記事「Javaスレッドプールを不適切に使用するとどうなるか - 本番環境のケース」では、Controllerによって呼び出されるメソッド内にスレッドプールを作成し、非同期タスクを実行するという企業の本番環境ケースを紹介しました。この場合、毎回スレッド プールが作成され、スレッド プールは破棄されないため、メソッドが呼び出されるたびに、少なくとも 1 つのスレッドが作成されます。メソッドが 1000 回呼び出されると、1000 個のスレッドが作成されます。
@Service
@Slf4j
public class AccountAuthServiceImpl implements AccountAuthService {
//省略了部分代码
//功能:将两个系统账号进行绑定
@Override
public boolean bindUser(Long hrsAccountId, Long hmcAccountId){
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2);
Future<Boolean> hrsExt = executorService.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
//查询hrsAccountId是否存在
return true;
}
});
Future<Boolean> hmcExt = executorService.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
//查询hmcAccountId是否存在
return true;
}
});
try {
return hrsExt.get(3, TimeUnit.SECONDS) & hmcExt.get(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return false;
}
}
4. まとめ
- スレッド プールとスレッドは、問題が発生したときにトラブルシューティングを容易にするために、開発者になじみのある名前で設定する必要があります. これは、他のプロジェクトや Java のコマンドで見つけることができます. スレッドの命名は非常に重要なステップです. Java はデフォルト名を提供しますが、トラブルシューティングではカスタム名が特に重要です
- スレッド プールのタスク キュー容量を設定する必要があります。そうしないと、メモリの消費やサービスのダウンタイムが発生する可能性があります
- スレッドプールの作成はビジネスメソッドで作成できません