web-dev-qa-db-ja.com

スリープして状態が真になるまで確認する

Javaに以下を実行するライブラリはありますか?threadは、条件がtrueになるか、最大時間に達するまで、xミリ秒間sleepを繰り返し実行する必要があります。

この状況は主に、テストがある条件が真になるのをテストが待っているときに発生します。条件は別のthreadの影響を受けています。

[編集]明確にするために、テストが失敗するまでにXミリ秒だけ待機するようにしたいと思います。条件が真になるのを永遠に待つことはできません。不自然な例を追加しています。

class StateHolder{
    boolean active = false;
    StateHolder(){
        new Thread(new Runnable(){
            public void run(){
                active = true;
            }
        }, "State-Changer").start()
    }
    boolean isActive(){
        return active;
    }
}


class StateHolderTest{
    @Test
    public void shouldTurnActive(){
        StateHolder holder = new StateHolder();
        assertTrue(holder.isActive); // i want this call not to fail 
    }
}
22
Rag

[〜#〜]編集[〜#〜]

ほとんどの回答は、待機と通知または条件(多かれ少なかれ同じように機能します)を使用した低レベルAPIに焦点を当てています。慣れていない場合、正しく理解することは困難です。証明:これらの回答のうち2つは、待機を正しく使用しません。
_Java.util.concurrent_ は、これらすべての複雑さが隠されている高レベルのAPIを提供します。

私見、コンカレントパッケージに同じことを実現する組み込みクラスがある場合、待機/通知パターンを使用しても意味がありません。


CountDownLatch の初期カウントが1の場合、正確に次のようになります。

  • 条件がtrueになったときに、latch.countdown();を呼び出します
  • 待機中のスレッドで次を使用します:boolean ok = latch.await(1, TimeUnit.SECONDS);

考案された例:

_final CountDownLatch done = new CountDownLatch(1);

new Thread(new Runnable() {

    @Override
    public void run() {
        longProcessing();
        done.countDown();
    }
}).start();

//in your waiting thread:
boolean processingCompleteWithin1Second = done.await(1, TimeUnit.SECONDS);
_

注:CountDownLatchesはスレッドセーフです。

21
assylias

Awaitility はシンプルでクリーンなソリューションを提供します:

await().atMost(10, SECONDS).until(() -> condition());
10
thisismydesign

Condition を使用する必要があります。

条件に加えてタイムアウトを設定したい場合は、 await(long time, TimeUnit unit) を参照してください

8
Alex DiCarlo

Awaitilityが提供するようなソリューションを探していました。私の質問では間違った例を選んだと思います。私が意味したのは、サードパーティのサービスによって作成される非同期イベントの発生を予期していて、クライアントがサービスを変更して通知を提供できない状況でした。より合理的な例は次のようなものです。

class ThirdPartyService {

    ThirdPartyService() {
        new Thread() {

            public void run() {
                ServerSocket serverSocket = new ServerSocket(300);
                Socket socket = serverSocket.accept();
                // ... handle socket ...
            }
        }.start();
    }
}

class ThirdPartyTest {

    @Before
    public void startThirdPartyService() {
        new ThirdPartyService();
    }

    @Test
    public void assertThirdPartyServiceBecomesAvailableForService() {
        Client client = new Client();
        Awaitility.await().atMost(50, SECONDS).untilCall(to(client).canConnectTo(300), equalTo(true));
    }
}

class Client {

    public boolean canConnect(int port) {
        try {
            Socket socket = new Socket(port);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
1
Rag

寝てチェックして寝てチェックしてはいけません。条件変数で待機し、条件を変更して、何かを実行するときにスレッドを起動したいとします。

0
jco.owens

私はこの機能的なインターフェイスを利用したこの普遍的な方法を適用することによってこの問題を解決しました:

private void waitUntilConditionIsMet(BooleanSupplier awaitedCondition, int timeoutInSec) {
    boolean done;
    long startTime = System.currentTimeMillis();
    do {
        done = awaitedCondition.getAsBoolean();
    } while (!done && System.currentTimeMillis() - startTime < timeoutInSec * 1000);
}

次に、以下を実装できます。

class StateHolderTest{
    @Test
    public void shouldTurnActive(){
        StateHolder holder = new StateHolder();
        waitUntilConditionIsMet(() -> holder.isActive(), 10);
        assertTrue(holder.isActive);
    }
}
0
Igor Lopatka