5

バグを見つけられますか?これにより、がスローされjava.lang.OutOfMemoryErrorます。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestTheads {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        while(true) {
            executorService.submit(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }
                }
            });
        }
    }

}

バグは、無視しているオブジェクトを返すため、executorService.submit()の代わりに呼び出すことです。を使用すると、このプログラムは実際には永久に実行されます。executorService.execute()submit()Futureexecute()

ただし、 :execute()を使用する場合のように、メソッドを持つという贅沢が常にあるとは限りません。ScheduledExecutorService

public static void main(String[] args) {
    // this will FAIL because I ignore the ScheduledFuture object
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
    while(true) {
        executorService.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
}

何も返さず、計算するだけのタスクで何をすべきでしょうか?

どんなアイデアでもありがたいです!

編集ThreadPoolExecutorspurge()は有望に見えましたが、キャンセルされたタスクのみを削除します。

4

2 に答える 2

9

返されるオブジェクトは、実行されるまでのみFutureによって強く参照されます。(実際には、あなたに委任するインスタンスです。)実行されると、呼び出し元はそれを参照しないため、ガベージコレクションされます。言い換えれば、メモリの問題は、の処理とは何の関係もありません。ExecutorService FutureTaskRunnableFuture

メモリが不足している場合は、ワークキューに何百万ものタスクがキューに入れられていることが原因です。他のキューと同様に、平均消費率が平均生産率を超えない限り、キューはいっぱいになります。キューの内容はメモリを消費します。

制限付きキューを使用します。これにより、タスクキューイングが効果的に抑制されたり、メモリが増えたりします。

このコードは「永久に」実行されます。

  ExecutorService executorService = Executors.newFixedThreadPool(1);
  while(true) {
    executorService.submit(new Runnable() {
      public void run() {
        try {
          Thread.sleep(10);
        } catch (InterruptedException e) { }
      }
    });
    Thread.sleep(12);
  }

違いは、結果のFutureインスタンスの処理にはありませんが、タスクは処理可能な速度でキューに入れられます。

于 2011-04-28T22:29:46.210 に答える
7

あなたの問題は、返される先物ではありません。問題は、送信するRunnableごとに、ExecutorServiceがそれらを保存して後で処理できるようにすることです。Runnable(またはFuture)を使用してsubmitメソッドを呼び出すたびに、ExecutorServiceはそのrunnableをワーカーキューにプッシュします。Runnableは、スレッドがそのRunnableをキューから選択できるようになるまで(後で)そこに留まります。すべてのワーカースレッドがビジーの場合、ExecutorServiceはランナブルを上記のキューに配置するだけです。

したがって、問題は、別のスレッドによって無限に追加されているキューをプルしようとしているスレッドが1つしかないことです。ワーカースレッドが各Runnableを処理できるよりもはるかに高速に追加されます。

編集:nosがとらえられていないとして示したコード例は、実際にはRejectedExecutionExceptionをスローするため、選択する場合、スロットルのメカニズムは少し異なる必要があります。

私のコメントで述べたように、より良い解決策に関しては、ワーカースレッドがキューに追いつかないような方法でExecutorServiceをいっぱいにすることを期待している場合は、要求が入ってくるときにシリアル化および逆シリアル化できます(独自のThreadPoolExecutorを構築します)が、そのような必要性を確認しますケースは絶対に必要です。

作業が完了した後、それらのFutureは破棄され、ガベージコレクションされることに注意してください。したがって、1秒間に1つのFutureを実行し、1秒以内に実行すると、Future自体が削除され、メモリの問題は発生しません。ただし、1秒間に1つのフューチャーを実行し、スレッドが3秒ごとにフューチャーを実行する場合、それは描画されて発行されます。

編集:実行しているプログラムのヒープのプロファイルを作成しましたが、問題はまさにそれです。ExecutorServiceによって作成されているFutureTaskは、ワーカースレッドがそれを選択するまでワーカーキューにあります。

Class Name                                                       | Shallow Heap | Retained Heap | Percentage 
------------------------------------------------------------------------------------------------------------
java.util.concurrent.ThreadPoolExecutor @ 0x78513c5a0            |          104 | 2,051,298,872 |     99.99% 
|- java.util.concurrent.LinkedBlockingQueue @ 0x785140598        |           80 | 2,051,298,216 |     99.99% 
|  |- java.util.concurrent.LinkedBlockingQueue$Node @ 0x785142dd8|           32 | 2,051,297,696 |     99.99% 
------------------------------------------------------------------------------------------------------------

ヒープ分析は少し進みます。想像できるように、LinkedBlockingQueue$Nodeはたくさんあります。

于 2011-04-28T21:51:14.797 に答える