8

ThreadPoolExecutorタスクを実行するためにを使用し ています。バックエンドは、SynchronousQueueであるため、エグゼキュータがすでにタスクを実行している場合は、をスローしRejectedExecutionExceptionます。簡単なテストケースは次のとおりです。

public class ExecutorTest {

  final static Worker worker = new Worker();

  public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());

    while (true) {
        try {                
            executor.execute(worker);                
        }catch (RejectedExecutionException e) {                
        }
    }        
  }

  static class Worker implements Runnable {

    private int i = 0;
    private long start = System.currentTimeMillis();

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(++i + " " + (System.currentTimeMillis() - start));
        } catch (InterruptedException ex) {                
        }
    }
  }
}

予想される動作は次のとおりです。ワーカーを実行し、1秒間スリープした後、i(ワーカーがこれまでに実行された頻度を表す)とワーカーが作成されてからのミリ秒数を出力します。だから私は期待しています:

1 1015 
2 2015
3 3016 
4 4017

これはしばらくは問題なく動作しますが、ほぼ1時間後には次のようになります。

2919 2922196
2920 2942951
2921 2990407

したがって、あるワーカーの実行から次のワーカーの実行までの時間は、20秒(2919-> 2920)および38秒(2920-> 2921)などになります。すべてが非常に遅くなり、jvmはガベージコレクションに多くの時間を費やします。最後に(数日後)OutOfMemoryErrorが発生しました。

64ビットLinuxマシン上のOracleのJVM1.7.0_07で、これを-Xmx8M(ヒープスペースが増えると効果がかなり後で現れると思います)で実行しています。ポインタをいただければ幸いですが、おそらく私は明らかなことを見逃しているだけです。

4

1 に答える 1

2

ThreadPoolExecutorインスタンス化の変更を試みることができます。コンストラクターに引数を追加して、RejectExecutionHandler拒否されたタスクを黙って破棄する a を使用するだけです。

public static void main(String[] args) {
  ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());

  while (true) {
    executor.execute(worker);                
  }
}

したがって、問題が繰り返し発生する場合RejectedExecutionException(私はそう思います)、それを避けることができます。

于 2012-10-16T13:17:28.427 に答える