2012-10-17 9 views
6

私は、URLにGETリクエストをたくさん送信する必要があるプログラムを作成しました。これはできるだけ速くする必要があります。最初にプログラムを作成したとき、私は接続をforループに入れましたが、続行する前に各接続が完了するのを待たなければならないため、実際には遅かったです。私はそれを速くしたいので、スレッドを使ってみましたが、やや高速でしたが、まだ満足していません。スレッドを理解する+非同期

私はこのことについて、正しい方法を推測しています。これを実際に高速化するには、非同期接続を使用してすべてのURLに接続する必要があります。これは正しいアプローチですか?

また、私はスレッドを理解しようとしており、どのように動作するのですか、それを得ることはできません。私のコンピュータにはIntel Core i7-3610QMクアッドコアプロセッサが搭載されています。このプロセッサの仕様に関するインテルの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つ以上のスレッドを追加すると、2以上のすべてのスレッドで同じことが起こります。

申し訳ありませんが、これは長い読まれた場合、私はたくさんの質問をしました。 ありがとうございます。

答えて

9

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

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

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

パフォーマンスに関するご質問にお答えします。接続をオープンしてもスリープが発生しない場合、同期または非同期に処理する場合はパフォーマンスが8スレッドを超えることはできません。実際には、接続を開くのにかかわる多くの時間が睡眠に費やされます。非同期と同期の違いは、スリープに費やされた時間を処理する方法です。理論的には、両者のパフォーマンスはほぼ同じになるはずです。

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

非同期モデルでは、コアごとに1つのスレッドしか作成しません。そのスレッドがスリープする必要がある場合、スリープしませんが、実際には次の接続への切り替えを処理するコードが必要です。あなたはコアを持っているよりも多くのスレッドを有するにはポイントが存在しないので、どの時点でスレッドの睡眠を行うこと

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); 
    } 
    } 
} 

注意:例えば、接続(A、B、C)の開口部に三つのステップが存在すると仮定する。最終的には、同期アプローチか非同期アプローチのどちらを使用するかは、個人の好みの問題です。絶対的な極端な点においてのみ、両者のパフォーマンスの違いがあります。アプリケーションのボトルネックとなるポイントに達するためには、プロファイリングに長い時間を費やす必要があります。

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

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

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

+0

お返事ありがとうございます。 編集中... – user1203585

+0

ああ、実際にコメントを編集できません... 5分制限... "すべてのスレッドが共有するオブジェクトはありますか?このオブジェクトには同期メソッドがありますか? 私のすべてのスレッドは同じことをしています: URLオブジェクトをインスタンス化し、プロキシとの接続を開きます。 URLConnectionの接続タイムアウトと読み取りタイムアウトを設定します。その後、BufferedReaderとInputStreamReaderを使用してURLConnectionから読み取ります。最後に、テキストファイルに単語を書き込みます。 これは、各スレッドが実行しているスレッドのうち、500スレッドを実行しても速度が上がらないようです。/ – user1203585

+1

Javaにはサイズが制限されている基盤接続プールがあると思われます。 http.maxConnections [http://docs.oracle.com/javase/1.4.2/docs/guide/net/properties.html]というネットワーキング・プロパティーがあります。デフォルトは5です。これは、5つ以上の接続が開いた後で、それらがすべて同じ5つの基本ソケット(共有リソース)を使用していることを意味します。再度、JVisualVMを使用してこれを確認できます。 – Pace

1

いくつかの概念:

あなたは、システム内の多くのスレッドを持つことができますが、その一部のみ(あなたの場合は最大8)は、時間の任意の時点でCPUの「定時」になります。したがって、並列に実行されている8つのスレッドよりもパフォーマンスを上げることはできません。実際には、スレッドの作成、破棄、および管理に関連する作業のために、スレッドの数を増やすとパフォーマンスが低下する可能性があります。

スレッドは異なる状態になる可能性があります。http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html これらの状態のうち、RUNNABLEスレッドはCPU時間のスライスを取得します。オペレーティングシステムはCPU時間のスレッドへの割り当てを決定します。 1000のスレッドを持つ通常のシステムでは、特定のスレッドがCPU時間を取得し、CPU上にどれくらいの時間がかかるかは完全に予測できません。 - 並列非同期ネットワーク要求を行う

をあなたは正しい解決策を考え出したようです:あなたは解決している問題について

。しかし、事実上、10000以上のスレッドを開始し、同時に多くのネットワーク接続がシステムリソースに負担をかける可能性があり、うまく動作しない可能性があります。このpostには、Javaを使用した非同期I/Oに関する多くの提案があります。 (ヒント:受け入れられた回答だけではありません)

0

この解決策は、できるだけ速く10kリクエストを作成しようとする一般的な問題に特有です。私はあなたがJava HTTPライブラリを放棄し、代わりにApacheのHttpClientを使用することをお勧めします。それらは、パフォーマンスを最大化するためにいくつかの有用なものがあるかもしれません(suggestions)。私はApache HttpClientライブラリが一般的にはるかに速く、軽量でオーバーヘッドが少ないと聞いています。

関連する問題