6

そのため、URL に大量 (10,000 件以上) の GET 要求を送信する必要があるプログラムを作成しましたが、できるだけ高速にする必要があります。最初にプログラムを作成したとき、接続を for ループに入れただけでしたが、続行する前に各接続が完了するのを待たなければならないため、非常に遅くなりました。もっと速くしたかったので、スレッドを使ってみたところ、多少速くなりましたが、まだ満足していません。

これを行う正しい方法は、非同期接続を使用してすべての URL に接続することだと思います。これは正しいアプローチですか?

また、スレッドとその仕組みを理解しようとしていますが、理解できないようです。私が使用しているコンピューターには、Intel Core i7-3610QM クアッドコア プロセッサが搭載されています。このプロセッサの仕様に関する Intel の Web サイトによると、8 つのスレッドがあります。これは、Java アプリケーションで 8 つのスレッドを作成でき、それらはすべて同時に実行されるということですか? 8 を超えると速度が向上しませんか?

タスク マネージャーの [パフォーマンス] タブの [スレッド] の横にある数字は正確には何を表していますか? 現在、私のタスク マネージャーは、「スレッド」が 1,000 を超えていると表示しています。なぜこの数字なのか、プロセッサがサポートしているのが 8 を超えるのはなぜでしょうか? また、テストとして 500 スレッドでプログラムを試したところ、タスク マネージャーの数が 500 増加しましたが、代わりに 8 スレッドを使用するように設定した場合と同じ速度であることに気付きました。Java アプリケーションで使用しているスレッドの数に応じて数が増加している場合、速度が同じなのはなぜですか?

また、Java でスレッドを使用して小さなテストを試みましたが、出力が意味を成しません。ここに私のテストクラスがあります:

import java.text.SimpleDateFormat;
import java.util.Date;

public class Test {

    private static int numThreads = 3;
    private static int numLoops = 100000;
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("[hh:mm:ss] ");

    public static void main(String[] args) throws Exception {

        for (int i=1; i<=numThreads; i++) {
            final int threadNum = i;
            new Thread(new Runnable() {
                public void run() {
                    System.out.println(dateFormat.format(new Date()) + "Start of thread: " + threadNum);
                    for (int i=0; i<numLoops; i++)
                        for (int j=0; j<numLoops; j++);
                    System.out.println(dateFormat.format(new Date()) + "End of thread: " + threadNum);
            }
            }).start();
            Thread.sleep(2000);
        }

    }
}

これにより、次のような出力が生成されます。

[09:48:51] Start of thread: 1
[09:48:53] Start of thread: 2
[09:48:55] Start of thread: 3
[09:48:55] End of thread: 3
[09:48:56] End of thread: 1
[09:48:58] End of thread: 2

1 番目と 2 番目のスレッドがそれぞれ 5 秒かかるのに、3 番目のスレッドがすぐに開始および終了するのはなぜですか? 3 つ以上のスレッドを追加すると、2 つを超えるすべてのスレッドで同じことが起こります。

質問が多くて長文になってしまい申し訳ありません。前もって感謝します。

4

3 に答える 3

10

プロセッサにはスレッドではなく 8 つのコアがあります。これは、実際には、特定の瞬間に実行できるのは 8 つだけであることを意味します。ただし、これは、8 スレッドのみに制限されているという意味ではありません。

スレッドが URL への接続を同期的に開いている場合、リモート サーバーがスレッドに戻るのを待つ間、スレッドはスリープ状態になることがよくあります。そのスレッドがスリープしている間、他のスレッドが作業を行うことができます。500 個のスレッドがあり、500 個すべてがスリープしている場合、CPU のコアを使用していません。

反対に、500 個のスレッドがあり、500 個のスレッドすべてが何かを実行したい場合、すべてを一度に実行することはできません。このシナリオを処理するために、特別なツールがあります。プロセッサ (またはオペレーティング システムまたはその 2 つの組み合わせ) にはスケジューラがあり、プロセッサ上でどのスレッドをアクティブに実行するかを決定します。これらのスケジューラーの動作を制御するさまざまなルールと、場合によってはランダムなアクティビティがあります。これは、上記の例でスレッド 3 が常に最初に終了するように見える理由を説明している可能性があります。メインスレッドによってスケジュールされた最新のスレッドであるため、スケジューラがスレッド 3 を優先している可能性があります。動作を予測できない場合があります。

次に、パフォーマンスに関する質問に答えます。接続を開くときにスリープが発生しない場合は、物事を同期的に処理するか非同期的に処理するかに関係なく、8 スレッドを超えるパフォーマンスを向上させることはできません。実際には、接続を開くために必要な時間の多くは睡眠に費やされています。非同期と同期の違いは、スリープに費やされた時間を処理する方法です。理論的には、2 つの間でほぼ同等のパフォーマンスを得ることができるはずです。

マルチスレッド モデルでは、コアよりも多くのスレッドを作成するだけです。スレッドがスリープ状態になると、他のスレッドが機能します。これは、スケジューリングやスレッド間の対話を記述する必要がないため、扱いが簡単な場合があります。

非同期モデルでは、コアごとに 1 つのスレッドのみを作成します。そのスレッドがスリープする必要がある場合、スレッドはスリープしませんが、実際には次の接続への切り替えを処理するコードが必要です。たとえば、接続を開くには 3 つの手順 (A、B、C) があるとします。

while (!connectionsList.isEmpty()) {
  for(Connection connection : connectionsList) {

    if connection.getState() == READY_FOR_A {
      connection.stepA();
      //this method should return immediately and the connection
      //should go into the waiting state for some time before going
      //into the READY_FOR_B state
    }
    if connection.getState() == READY_FOR_B {
      connection.stepB();
      //same immediate return behavior as above
    }
    if connection.getState() == READY_FOR_C {
      connection.stepC();
      //same immediate return behavior as above
    }
    if connection.getState() == WAITING {
      //Do nothing, skip over
    }
    if connection.getState() == FINISHED {
      connectionsList.remove(connection);  
    }
  }
}

スレッドがスリープ状態になることは決してないため、コアよりも多くのスレッドを使用しても意味がないことに注意してください。最終的に、同期アプローチと非同期アプローチのどちらを採用するかは、個人の好みの問題です。絶対的な極端な場合にのみ、2 つのパフォーマンスの違いが生じ、それがアプリケーションのボトルネックになるポイントに到達するには、プロファイリングに長い時間を費やす必要があります。

多くのスレッドを作成しているようですが、パフォーマンスが向上していないようです。これにはいくつかの理由が考えられます。

  • 接続の確立が実際にはスリープしていない可能性があります。その場合、8 スレッドを超えるパフォーマンスの向上は期待できません。これはありそうにないと思います。
  • すべてのスレッドが共通の共有リソースを使用している可能性があります。この場合、スリープ中のスレッドが共有リソースを持っているため、他のスレッドは機能しません。すべてのスレッドが共有するオブジェクトはありますか? このオブジェクトには同期メソッドがありますか?
  • 独自の同期がある可能性があります。これにより、上記の問題が発生する可能性があります。
  • 各スレッドが、複数のスレッドを使用することで得られる利点を台無しにする、ある種のセットアップ/割り当て作業を行う必要がある可能性があります。

私があなただったら、JVisualVM のようなツールを使用して、少数のスレッド (20) で実行しているアプリケーションをプロファイリングします。JVisualVM には、スレッドが実行中、ブロック中、またはスリープ中のときに表示される色付きのスレッド グラフがあります。これは、実行中のスレッドの数がコアの数より少ないことがわかるはずなので、スレッド/コアの関係を理解するのに役立ちます。さらに、ブロックされたスレッドが多数表示される場合は、ボトルネックにつながる可能性があります (ブロックされたスレッドが多数表示される場合は、その時点で JVisualVM を使用してスレッド ダンプを作成し、スレッドがブロックされている対象を確認します)。

于 2012-10-17T03:26:43.600 に答える
1

いくつかの概念:

システムには多くのスレッドを含めることができますが、いつでも CPU で「スケジュール」されるのはそのうちの一部 (この場合は最大 8 つ) だけです。したがって、8 つのスレッドを並行して実行する以上のパフォーマンスは得られません。実際、スレッドの作成、破棄、および管理に関連する作業のため、スレッドの数を増やすとパフォーマンスが低下する可能性があります。

スレッドはさまざまな状態になる可能性があります: http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html これらの状態のうち、RUNNABLE スレッドは、 CPU 時間。オペレーティング システムは、スレッドへの CPU 時間の割り当てを決定します。数千のスレッドを持つ通常のシステムでは、特定のスレッドがいつ CPU 時間を取得し、どれくらいの時間が CPU 上にあるかはまったく予測できません。

あなたが解決している問題について:

あなたは正しい解決策を見つけたようです-並列非同期ネットワークリクエストを作成します。ただし、実際には、10000 以上のスレッドと多数のネットワーク接続を同時に開始すると、システム リソースに負担がかかり、機能しない可能性があります。この投稿には、Java を使用した非同期 I/O に関する多くの提案があります。(ヒント:受け入れられた回答だけを見ないでください)

于 2012-10-17T03:44:24.420 に答える
0

このソリューションは、10kのリクエストをできるだけ速く実行しようとする一般的な問題に固有のものです。Java HTTPライブラリを放棄し、代わりにApacheのHttpClientを使用することをお勧めします。彼らは、役に立つかもしれないパフォーマンスを最大化するためのいくつかの提案を持っています。Apache HttpClientライブラリは、一般的にも高速で、軽量で、オーバーヘッドが少ないと聞いています。

于 2012-10-17T12:20:39.127 に答える