0

Executors.newFixedThreadPool(8)によって作成されたExecutorServiceの8つのスレッドで実行される5000の同様のCallableタスクがあります。各タスクはデータベースに移動して、処理する大量のデータを取得します。

すべてが99%の時間正常に動作しますが、DBが遅いかスタックしている場合(理由は聞かないでください)、ログファイルに非常に奇妙な実行ログメッセージが表示されることがあります。現在実行中の8つのタスクが停止し、8つのスレッドすべてでまだ完了していません。 、ExecutorServiceは、1つずつ実行するタスクの送信を開始します。

したがって、ログは、ある時点でExecutorServiceがクレイジーになり、前のタスクが完了するのを待たずに、待機キュー内のCallableのCallableのcall()メソッドの呼び出しを開始することを示しています。ますます多くのタスクがDBにリクエストを送信し、最終的にDBがひざまずき、Javaヒープメモリが使い果たされます。

ExecutorService内で何か奇妙なことが起こっているか、状況の私の理解が間違っているようです。誰かがそのようなものを見たことがありますか?

私の脳のスタックが溢れています

psは、JavaAPIからの引用です。

Executors.newFixedThreadPool(int nThreads)

共有の無制限キューで動作する固定数のスレッドを再利用するスレッドプールを作成します。どの時点でも、最大でnThreadsスレッドがアクティブな処理タスクになります。すべてのスレッドがアクティブなときに追加のタスクが送信されると、スレッドが使用可能になるまでキューで待機します。シャットダウン前の実行中に障害が発生したためにスレッドが終了した場合、後続のタスクを実行するために必要な場合は、新しいスレッドが代わりに使用されます。

これは実際に私のタスクによってスレッドが停止し、ExecutorServiceがより多くのスレッドを作成して新しい8つのタスクをそれらに送信し、それらが停止してExecutorServiceがさらに8つのスレッドを作成し、さらに8つのタスクを送信するということが起こりますか?

pss:Callableのcall()内の操作全体がtry catchで囲まれているため、操作内で例外が発生した場合、例外がキャプチャされてログに記録されます。これは何も起こっていません。呼び出しが呼び出されて戻ることはありませんが、次のタスクは1つずつ呼び出され、戻ることも終了することも、例外をスローすることもありません。

自分のタスクが原因でスレッドプール内のスレッドが停止しているのではないかと思います。どうすれば模倣できますか?

4

2 に答える 2

3

推測も試してみます。

  1. データベースからのデータのフェッチを含む5000のタスクを送信します。
  2. その後すぐに、必要な行/テーブルで激しいロック競合が発生します。たぶん、外部プロセスが書き込み専用のロックを取得しています。多分デッドロックがあります。
  3. 次々とタスクがブロックされ、共有/読み取りロックが付与されるのを待ちます。
  4. 8つのスレッドすべてが中断され、待機しているように見えますI/O
  5. その後すぐに、データベース/ DBドライバーは、タスクが共有ロックを待機しすぎていることに気付きます。まとめてLock Wait Timeout、タスクの例外を順番に渡します。
  6. したがって、次々に、タスクはキューから失敗し、待機中のタスクは実行に移されますが、再び失敗するだけです。

タスクの例外は、を停止しないことに注意してくださいExecutorService。そのタスクに完了のマークを付けて続行します。

この例を参照してください。

public class Foo {

    static class Task implements Callable<String> {
        private static AtomicInteger i = new AtomicInteger(1);

        public String call() throws Exception {
            i.incrementAndGet();
            if (i.get() % 2 != 0) {
                throw new RuntimeException("That's odd, I failed.");
            }
            return "I'm done";
        }
    }

    public static void main(String[] args) throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(2);
        List<Future<String>> futures = new ArrayList<Future<String>>();
        for (int i = 0; i < 5; i++) {
            futures.add(es.submit(new Task()));
        }
        for (Future<String> future : futures) {
            try {
                System.out.println(future.get());
            } catch (ExecutionException ee) {
                System.err.println(ee.getCause());
            }
        }
        es.shutdown();
    }
}

可能な出力:

I'm done
I'm done
I'm done
java.lang.RuntimeException: That's odd, I failed.
java.lang.RuntimeException: That's odd, I failed.
于 2011-11-03T05:48:02.950 に答える
0

これは単なる推測です(質問にコードがないことを考えると、推測に値すると思います):

ExecutorService.invokeAll(Collection<? extends Callable<T>> tasks)現在のタスクが例外をスローした場合、他のタスクに移動します。(あなたはinvokeAll()を使用していますか?同じ動作をしていると思いますsubmit(Callable<T> task)が、javadocからは明らかではありません)

Future.isDone()後続のタスクが実行を開始する前に、これらの「スタック」タスクが発生した場合に備えて確認できますか? 例外がスローされ、ログに表示されない可能性があります...

javadoc から:

完了したタスクは、通常どおり終了するか、例外をスローして終了する可能性があることに注意してください。

http://download.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html#invokeAll(java.util.Collection%29

この場合Callable.call()、メソッド定義内のすべての例外をキャッチしてログに記録できます。

HTH

于 2011-11-03T04:54:52.213 に答える