6

Javaアプリケーションでスレッドを使用して、(ネットワーク呼び出しを使用して)データを並列に取得しています。スレッドプールを作成し、スレッドサイズを(最大10-15)とするメソッドを持っていて、それらをネットワーク呼び出しに使用しています。そのメソッドをループから数回呼び出しています。Javaでスレッドをあまりにも多く作成する

このアプリケーションを低速マシン(3GB RAM、Pentium-IV)で動作させると、すべて正常に動作しますが、iMac(32GB RAM、i7プロセッサ)上で実行すると、時々約2500とメモリエラーをスローします。

私は、JVMが完成したスレッドをすぐにプールに戻さないと思っています。つまり、完了したスレッドはすぐに新しいスレッドを作成します。

さらにiMacでも、もし私がThread.sleep(1000)を持っていれば、上記のforループですべてうまく動作します。しかし約900スレッドを作成します。

は、以下のコード、このアプリケーションからのサンプルである:私はiMacのループについては、以下の使用して上記の方法を呼び出す場合

public ArrayList<String> getValuesForKeyFromMaps(String key, ArrayList<Meta> locations) throws InterruptedException, ExecutionException{ 

    int threadNum = locations.size(); // 10-15 at max 

    ExecutorService executor = Executors.newFixedThreadPool(threadNum); 
    List<FutureTask<ArrayList<String>>> taskList = new ArrayList<FutureTask<ArrayList<String>>>(); 

    for(final Meta location : locations){ 

     FutureTask<ArrayList<String>> futureTask_1 = new FutureTask<ArrayList<String>>(new Callable<ArrayList<String>>() { 
      public ArrayList<String> call() throws Exception { 
       // service call 
       return getValues(key, location); 
      } 
     }); 
     taskList.add(futureTask_1); 
     executor.execute(futureTask_1); 

    } 

    ArrayList<String> values = new ArrayList<String>(); 

    // Wait until all results are available and combine them at the same time 
    for (int j = 0; j < threadNum; j++) { 
     FutureTask<ArrayList<String>> futureTask = taskList.get(j); 
     values.addAll(futureTask.get()); 

    } 
    executor.shutdown(); 
    return values; 
} 

、そのは、約2500のスレッドを作成するので、メモリエラーをスロー。しかし、遅いマシンでうまく動作します。

for(String key : keySet){ 
     getValuesForKeyFromMaps(key, metaMap.get(key)); 
    } 

そして、以下のコードでは、iMacでは約900スレッドで動作します。

 for(String key : keySet){ 
     getValuesForKeyFromMaps(key, metaMap.get(key)); 
     Thread.sleep(200); //sleeping for 200ms 
    } 

私は1000ミリ秒にループのために上記に睡眠時間を増やす場合は、その作成のみ30-50スレッドとアプリケーションが正常に動作しています。

アプリケーションで許可されている最大スレッドを制御するにはどうすればよいですか。私は作成時に最大10-15のスレッドを使用することを意図していますが、Javaではたくさんのスレッドが作成されています。

+1

2500またはさらに900スレッドです。あなたはそれで何をしたいですか? –

+0

これを参照してください:http://stackoverflow.com/questions/763579/how-many-threads-can-a-java-vm-support –

+0

「900スレッドの作成」のようなものは表示されません。まず、いくつかのスレッドを 'locations.size()'に固定してエグゼキュータを作成します。次に、この場所のコレクションを繰り返し、場所ごとに1つのタスクを作成し、スレッドプールに追加します。この場所のサイズは、あなたが思ったよりもはるかに多いか、10-15(最大)のタスクしか作成していません。そして... ...ところで...このコードスニペットの外にスレッドプールを作成してください。 – Seelenvirtuose

答えて

4

これはあまりにも多くのスレッドを作成しているJavaではありません。

関数を呼び出すたびにエグゼキュータを作成しないでください。それぞれ100個の要素で100個のコレクションがある場合は、10000スレッドを作成します。これは非常にリソースを消費します...そして無意味です。

ExecutorService executor = Executors.newFixedThreadPool(threadNum); 

8コアのエグゼキュータを作成し、どこにでも使用できます。あなたのコードはより速く動作し、アプリケーションは消費するリソースが少なくて済みます。

このcode review singleton executorの質問に慣れてください。あなたはあなたのアプリでそのソリューションを使用することができるかもしれません。

+0

これは良い提案ですが、 'threadNum'の初期化方法を知るためにソースコードを見てください。私はOPの問題の一部だと思う。 – Seelenvirtuose

+0

実際、問題の根源はスレッドを適切に活用する方法を理解していないことです。しかし、このサイトは質問に答えることであり、OPが彼が多すぎるスレッドを作成していることを疑っても、私はまっすぐ答えを出しました。彼に習います。 – Dariusz

3

ExecutorService executor = Executors.newFixedThreadPool(threadNum); を使用すると、getValuesForKeyFromMapsの呼び出しごとに新しいスレッドプールが作成されます。したがって、keySetに100個のエントリが含まれていると、それぞれ10-15個のスレッドを持つ100個のプールになります。 1つのスレッドプールをインスタンス変数またはクラス変数として保持し、必要に応じて使用します。

+0

これは良い提案ですが、 'threadNum'がどのように初期化されているかを調べるためにソースコードを見てください。私はOPの問題の一部だと思う。 – Seelenvirtuose

+0

@Seelenvirtuoseこれは問題の一部かもしれませんが、複数のスレッドプールについての点は確かです。 'sleep()'を追加することで、古いエグゼキュータを新しいものを作成する前にクリーンアップすることができるので、このことにもヒントがあります。 –

関連する問題