2012-05-26 30 views
5

Tôi sử dụng CountDownLatch để chờ một sự kiện nhất định từ một thành phần khác (chạy trong một chuỗi khác). Các phương pháp sau đây sẽ phù hợp với ngữ nghĩa của phần mềm của tôi, nhưng tôi không chắc liệu nó hoạt động như một tôi mong đợi:Nhiều cuộc gọi đến CountDownLatch.await (int) với timeout

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

Kịch bản nên là như sau: Tôi chờ đợi trong 3 giây, và nếu chốt là không được tính đến 0, tôi thông báo cho thành phần khác với biến đó, và sau đó tôi đợi tối đa 3,5 giây. Nếu có thời gian chờ một lần nữa, sau đó tôi không quan tâm và sẽ tiến hành các hoạt động khác.

Lưu ý: Tôi biết nó không giống như vậy, nhưng kịch bản trên hoàn toàn hợp lý và hợp lệ trong phần mềm của tôi.

Tôi đã đọc tài liệu chờ đợi (int, TimeUnit) và CountDownLatch, nhưng tôi không phải là chuyên gia Java/Android, vì vậy tôi cần xác nhận. Đối với tôi, tất cả các kịch bản tìm kiếm hợp lệ:

  • Nếu chờ đợi đầu tiên thành công, sau đó người kia chờ đợi sẽ trở lại ngay lập tức
  • Nếu chờ đợi đầu tiên lần ra ngoài, thì chờ đợi khác là vẫn còn hiệu lực; do đó, nếu các chủ đề khác chú ý đến tín hiệu tĩnh, thứ hai chờ đợi có thể trở thành công
  • Cả hai đang chờ đợi cuộc gọi thời gian ra (điều này là tốt theo ngữ nghĩa phần mềm của tôi)

Tôi có sử dụng chờ đợi (.. .) đúng không? Có thể một giây chờ đợi (...) được sử dụng theo cách trên ngay cả khi một chờ đợi trước đó (...) trên cùng một đối tượng hết thời gian?

Trả lời

4

Nếu tôi hiểu câu hỏi của bạn một cách chính xác, kiểm tra này chứng minh rằng tất cả các giả định/yêu cầu của bạn là đúng/đáp ứng. (Chạy với JUnit và hamcrest.) Lưu ý mã của bạn trong phương pháp runCodeUnderTest(), mặc dù nó được xen kẽ với ghi âm thời gian và timeout được giảm theo hệ số 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(), 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); 
     } 
    } 
} 
+0

Cám ơn các mã rất kỹ lưỡng. Trên thực tế, câu hỏi của tôi đơn giản hơn nhiều so với điều này, nhưng nếu tôi đúng, bạn thực sự * đã chứng minh * rằng giả định của tôi là chính xác, vì vậy một lời cảm ơn lớn cho điều đó! Câu hỏi cốt lõi của tôi chỉ là điều này: nếu lần đầu tiên chờ đợi() ** lần ** đối với một đối tượng, tôi có thể gọi await() một lần nữa trên cùng một đối tượng (như trong mã của tôi) không? Tôi cho là có, nhưng như tôi đã nói, tôi không phải là một chuyên gia Java. (Sau khi tôi nhận được xác nhận cuối cùng, tôi chắc chắn sẽ đánh dấu câu trả lời của bạn là câu trả lời, bởi vì nó nhiều hơn những gì tôi cần.) –

+1

Heh, thành thật mà nói, tôi không chắc chắn 100% câu trả lời ở đầu đầu của tôi, và cách tốt nhất để tìm câu trả lời như thế là thử nó. Do đó thử nghiệm :) –

Các vấn đề liên quan