我使用CountDownLatch来等待来自另一个组件的某个事件(在不同的线程中运行).以下方法适合我的软件的语义,但我不确定它是否像我期望的那样工作:
mCountDownLatch.await(3000,TimeUnit.MILLISECONDS)
otherComponent.aStaticVolatileVariable = true;
mCountDownLatch.await(3500,TimeUnit.MILLISECONDS);
...
方案应该如下:我等待3秒钟,如果锁存器没有倒计数到0,我用该变量通知另一个组件,然后我等待最多3.5秒.如果再次出现超时,那么我不在乎并将继续进行其他操作.
注意:我知道它看起来不像那样,但上面的场景在我的软件中是完全合理且有效的.
我确实阅读了await(int,TimeUnit)和CountDownLatch的文档,但我不是Java / Android专家,所以我需要确认.对我来说,所有场景看起来都有效:
>如果第一次等待成功,则另一次等待将立即返回
>如果第一个等待超时,那么另一个等待仍然有效;
因此,如果另一个线程注意到静态信号,则第二个
等待可能会成功返回
>两个都等待调用超时(根据我的软件的语义,这很好)
我正确使用等待(…)吗?即使先前await(…)在同一个对象上超时,也能以上述方式使用第二个等待(…)吗?
最佳答案
如果我正确理解您的问题,此测试证明您的所有假设/要求都是真实/满足的. (使用JUnit和Hamcrest运行.)请注意runCodeUnderTest()方法中的代码,尽管它散布着时间记录,并且超时减少了10倍.
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(),10));
}
@Test
public void neverCountDown() throws Exception {
runCodeUnderTest();
assertThat((double) elapsedTime,closeTo(650,closeTo(350,10));
}
@Test
public void countDownAfterSecondTimeout() throws Exception {
countDownAfter(1000);
runCodeUnderTest();
assertThat((double) elapsedTime,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);
}
}
}