6

別のコンポーネント (別のスレッドで実行されている) からの特定のイベントを待機するために、CountDownLatch を使用します。次のアプローチは私のソフトウェアのセマンティクスに適合しますが、期待どおりに機能するかどうかはわかりません。

mCountDownLatch.await(3000, TimeUnit.MILLISECONDS)
otherComponent.aStaticVolatileVariable = true;
mCountDownLatch.await(3500, TimeUnit.MILLISECONDS);
... <proceed with other stuff>

シナリオは次のとおりです。3 秒間待機し、ラッチが 0 までカウントダウンされない場合は、その変数を使用して他のコンポーネントに通知し、最大 3.5 秒間待機します。再びタイムアウトが発生した場合は、気にせず、他の操作を続行します。

注: そのように見えないことはわかっていますが、上記のシナリオは完全に妥当であり、私のソフトウェアでは有効です。

await(int,TimeUnit) と CountDownLatch のドキュメントを読みましたが、Java/Android の専門家ではないため、確認が必要です。私には、すべてのシナリオが有効に見えます。

  • 最初の await が成功した場合、もう一方の await はすぐに戻ります
  • 最初の await がタイムアウトしても、もう 1 つの await は引き続き有効です。したがって、他のスレッドが静的シグナルに気付いた場合、2 番目の await が正常に戻る可能性があります。
  • どちらも await コールのタイムアウトです (私のソフトウェアのセマンティクスによれば、これは問題ありません)。

await(...) を正しく使用していますか? 同じオブジェクトの前の await(...) がタイムアウトした場合でも、2 番目の await(...) を上記の方法で使用できますか?

4

1 に答える 1

4

私があなたの質問を正しく理解している場合、このテストはあなたのすべての仮定/要件が正しい/満たされていることを証明します。(JUnitとHamcrestを使用して実行します。)runCodeUnderTest()メソッドのコードに注意してください。ただし、時間の記録が散在しており、タイムアウトは10分の1に短縮されています。

import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertThat;

public class CountdownLatchTest {
    static volatile boolean signal;
    CountDownLatch latch = new CountDownLatch(1);
    long elapsedTime;
    long[] wakeupTimes = new long[2];

    @Before
    public void setUp() throws Exception {
        signal = false;
    }

    @Test
    public void successfulCountDownDuringFirstAwait() throws Exception {
        countDownAfter(150);
        runCodeUnderTest();
        assertThat((double) elapsedTime, closeTo(150, 10));
        assertThat(wakeupTimeSeparation(), lessThan(10));
    }

    @Test
    public void successfulCountDownDuringSecondAwait() throws Exception {
        countDownAfter(450);
        runCodeUnderTest();
        assertThat((double) elapsedTime, closeTo(450, 10));
        assertThat((double) wakeupTimeSeparation(), closeTo(150, 10));
    }

    @Test
    public void neverCountDown() throws Exception {
        runCodeUnderTest();
        assertThat((double) elapsedTime, closeTo(650, 10));
        assertThat((double) wakeupTimeSeparation(), closeTo(350, 10));
    }

    @Test
    public void countDownAfterSecondTimeout() throws Exception {
        countDownAfter(1000);
        runCodeUnderTest();
        assertThat((double) elapsedTime, closeTo(650, 10));
        assertThat((double) wakeupTimeSeparation(), closeTo(350, 10));
    }

    @Test
    public void successfulCountDownFromSignalField() throws Exception {
        countDownAfterSignal();
        runCodeUnderTest();
        assertThat((double) elapsedTime, closeTo(300, 10));
    }

    private int wakeupTimeSeparation() {
        return (int) (wakeupTimes[1] - wakeupTimes[0]);
    }

    private void runCodeUnderTest() throws InterruptedException {
        long start = System.currentTimeMillis();
        latch.await(300, TimeUnit.MILLISECONDS);
        wakeupTimes[0] = System.currentTimeMillis();
        signal = true;
        latch.await(350, TimeUnit.MILLISECONDS);
        wakeupTimes[1] = System.currentTimeMillis();
        elapsedTime = wakeupTimes[1] - start;
    }

    private void countDownAfter(final long millis) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                sleep(millis);
                latch.countDown();
            }
        }).start();
    }

    private void countDownAfterSignal() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                boolean trying = true;
                while (trying) {
                    if (signal) {
                        latch.countDown();
                        trying = false;
                    }
                    sleep(5);
                }
            }
        }).start();
    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new IllegalStateException("Unexpected interrupt", e);
        }
    }
}
于 2012-05-26T23:34:36.677 に答える