web-dev-qa-db-ja.com

Java Future.get(timeout)が信頼できないのはなぜですか?

Future.get(timeout)は、指定されたタイムアウト後にTimeoutExceptionを確実にスローしません。これは正常な動作ですか、それともこれをより信頼できるものにするために何かできるでしょうか?このテストは私のマシンでは失敗します。しかし、2000ではなく3000で寝ると、合格します。

public class FutureTimeoutTest {
@Test
public void test() throws
    ExecutionException,
    InterruptedException {

    ExecutorService exec = Executors.newSingleThreadExecutor();
    final Callable call = new Callable() {
        @Override
        public Object call() throws Exception {
             try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            return 0;
        }
    };
    final Future future = exec.submit(call);
    try {
        future.get(1000, TimeUnit.MILLISECONDS);
        fail("expected TimeoutException");
    } catch (TimeoutException ignore) {
    }
}

}

27
Andrew Raphael

テストに合格することを期待する理由はありません。タスクを実行のために送信してから完了を待つ場合、Future#get()の待機が始まるまでに任意の時間が経過する可能性があり、タスクがスリープ時間を使い果たして完了するのに十分な時間が与えられます。

あなたの場合、Executor内で実行されているスレッドは、実行可能な状態であるにもかかわらず、test()を実行しているメインスレッドが保留されている間にフォーカスを取得すると想定できます。送信されたタスクを2秒間停止することと3秒間停止することの違いについては、コンピューターで他のプロセスがビジー状態になっていることによっては、3秒でも不十分な状況が見つかると思います。

15
seh

@sehは正しいです。

あなたは、Javaから一般に「リアルタイム」動作と呼ばれるものを期待しています。これは、リアルタイムオペレーティングシステムで実行されているリアルタイム対応のJavaディストリビューションでリアルタイムライブラリを使用しない限り、確実に実現することはできません。

説明のために、HotSpotなどの最新のJVMでのJavaスレッドの実装は、ホストオペレーティングシステムのネイティブスレッドスケジューラに依存して、いつ実行するスレッドを決定します。スレッドスケジューラが特にリアルタイムを認識していない限り、締め切りなどにより、いつ実行するスレッドを決定する際に「システム全体」のビューが表示される可能性があります。システムがロードされている場合、特定のスレッドが数秒以上実行されるようにスケジュールされない場合があります。実行を妨げる条件(タイマーイベントの待機など)が過ぎました。

次に、Java GCによって他のすべてのスレッドがブロックされる可能性があるという問題があります。

Javaからのリアルタイムの動作が本当に必要な場合は、それを利用できます。例えば:

ただし、リアルタイムの動作を提供するために、さまざまなAPIを使用するようにアプリケーションを変更することを期待する必要があります。

11
Stephen C

他の2つの回答は、現在Java同時実行クラスについて不必要に低い意見を持っていると思います。ミリ秒の精度(「実際の」リアルタイムアプリケーションが期待するもの)は得られません)しかし、通常は非常にうまく機能します。FuturesとExecutorsを使用して大規模な商用サービスを作成しましたが、負荷がかかっていても、通常は予想時間の10ミリ秒以内に機能しました。

このテストは、MacOS10.6とJava 1.6およびWinXPw/Java 1.6.0_22の両方で実行しましたが、どちらも期待どおりに機能します。

精度をテストするために、コードを次のように変更しました。

    long time1 = System.nanoTime();

    System.out.println("Submitting");
    final Future<Object> future = exec.submit(call);
    try {
        future.get(1000, TimeUnit.MILLISECONDS);

        long time2 = System.nanoTime();
        System.out.println("No timeout after " + 
                             (time2-time1)/1000000000.0 + " seconds");

        fail("expected TimeoutException");
    } catch (TimeoutException ignore) {
        long time2 = System.nanoTime();
        System.out.println("Timed out after " +
                             (time2-time1)/1000000000.0 + " seconds");
    }
    finally {
        exec.shutdown();
    }

XPでは「1.002598934秒後にタイムアウト」と出力され、MacOSXでは「1.003158秒後にタイムアウト」と出力されます。

元の投稿者がOSとJDKのバージョンを説明している場合、これが特定のバグであるかどうかを判断できる可能性があります。

7
andrewmu