1

この質問は、SingleThreadExecutor(JDK 1.6) を使用した場合の影響に関するものです。関連する質問がこのフォーラムで以前に尋ねられ、回答されましたが、私が直面している状況は少し異なると思います.

アプリケーションのさまざまなコンポーネント (コンポーネント C1、C2、C3 などと呼びましょう) は、(アウトバウンド) メッセージを生成します。ほとんどの場合、他のコンポーネントから受信したメッセージ (インバウンド) に応答します。これらの送信メッセージは、通常はArrayBlockingQueueインスタンスであるキューに保持されます - おそらくかなり標準的な方法です。ただし、送信メッセージは追加された順序で処理する必要があります。SingleThreadExectorここでは a の使用が明らかな答えだと思います。最終的には 1 対 1 の状況、つまり1 つのキュー ( 1 つのコンポーネントから発信されるメッセージ専用)SingleThreadExecutorに対して1 つになります。

ここで、コンポーネント (C1、C2、C3...) の数は、特定の時点では不明です。それらはユーザーの必要に応じて存在します (最終的には破棄されます)。ピーク負荷時の 200 ~ 300 個のコンポーネントについて話しています。上記の 1:1 設計原則に従って、200SingleThreadExecutor秒を配置します。これが私のクエリのソースです。

非常に多くのSingleThreadExecutorを作成しなければならないという考えに不快感を覚えます。私はむしろSingleThreadExecutorのプールを試して使用したいと思います. ここでの推奨される使用法に関する多くの投稿を読みましたSingleThreadExecutorが、同じプールはどうですか?

ここにいる学識のある女性と男性は何を考えていますか?私は指示、修正、または単に忠告されたいです:-)。

4

4 に答える 4

1

投稿された順序でメッセージを処理することが要件である場合、必要なのは 1 つだけSingleThreadExecutorです。複数のエグゼキューターがある場合、メッセージはエグゼキューターのセット全体で順不同で処理されます。

単一のプロデューサーに対して受信した順序でのみメッセージを処理する必要がある場合は、プロデューサーごとに 1 つのエグゼキューターを用意するのが理にかなっています。エグゼキュータをプールしようとすると、プロデューサーとエグゼキュータの間のアフィニティを確保するために多くの作業を行う必要があります。

プロデューサーがライフタイムを定義することを示しているため、確実に行う必要があることの 1 つは、完了時にエグゼキューターを適切にシャットダウンすることです。

于 2009-12-22T02:06:21.140 に答える
0

そこには何の問題もありません。基本的に、独立したキューがあり、それぞれを順番に排出する必要があります。それぞれに1つのスレッドが自然な設計です。あなたが思いつくことができる他のものは本質的に同じです。例として、Java NIOが最初に登場したとき、フレームワークはそれを利用して、要求ごとのスレッドモデルから脱却しようとして作成されました。結局、何人かの著者は、良いプログラミングモデルを提供するために、スレッドをもう一度再実装しているだけだと認めました。

于 2009-12-22T05:54:57.433 に答える
0

アプリケーションについて詳しく知らなければ、300 または 3000 のスレッドで問題が発生するかどうかを判断することは不可能です。複雑さを増す前に、アプリケーションをプロファイリングすることを強くお勧めします

最初に確認する必要があるのは、同時に実行されているスレッドの数が、それらのスレッドを実行するために使用できるコアの数よりも大きくならないようにすることです。アクティブなスレッドが多ければ多いほど、それらのスレッドの管理に費やされる時間が増え (コンテキストの切り替えにコストがかかります)、実行される作業が少なくなります。

実行中のスレッドの数を制限する最も簡単な方法は、セマフォを使用することです。作業開始前にセマフォを取得し、作業終了後に解放する。

残念ながら、実行中のスレッドの数を制限するだけでは不十分な場合があります。コンテキスト切り替えごとに費やされる時間が 1 つの作業単位の総コストの大部分を占める場合、それは役立つかもしれませんが、それでもオーバーヘッドが大きくなる可能性があります。このシナリオでは、多くの場合、最も効率的な方法はキューの数を固定することです。キューの選択にラウンドロビンなどのアルゴリズムを使用してコンポーネントを初期化するときに、キューのグローバル プールからキューを取得します。

最も明白な解決策が機能しない不幸なケースの 1 つである場合は、比較的単純なものから始めます。1 つのスレッド プール、1 つの同時キュー、ロック、キューのリスト、およびプール内の各スレッドの一時キューです。

キューへの作業の投稿は簡単です。ペイロードとプロデューサーの ID を追加します。

処理も比較的簡単です。まず、キューから次のアイテムを取得します。次に、ロックを取得します。ロックを設定している間、他のスレッドが同じプロデューサーのタスクを実行しているかどうかを確認します。そうでない場合は、キューのリストに一時キューを追加してスレッドを登録します。それ以外の場合は、タスクを既存の一時キューに追加します。最後にロックを解除します。現在のスレッドがタスクを実行するために登録されているかどうかに応じて、タスクを実行するか、次をポーリングして最初からやり直します。タスクを実行した後、再びロックを取得し、一時キューにさらに作業があるかどうかを確認します。そうでない場合は、リストからキューを削除します。それ以外の場合は、次のタスクを取得します。最後にロックを解除します。ここでも、タスクを実行するか、最初からやり直すかを選択します。

于 2016-03-16T10:33:52.593 に答える
0

メッセージングとバッチ ジョブは、何度も何度も解決されてきたものです。二度と解決しようとしないことをお勧めします。代わりに、スレッド プールを維持し、データベースにタスクを永続化する Quartz を調べてください。または、JMS/ActiveMQ を調べたほうがよいかもしれません。ただし、まだ行っていない場合は、少なくとも Quartz を調べてください。ああ、Spring は Quartz での作業をとても簡単にします...

于 2009-12-22T04:15:13.673 に答える