随着处理器比以往包装更多的内核,并发编程已成为最有效利用它们的最前沿。但是,事实是并发程序的设计,编写,测试和维护要困难得多。因此,如果我们毕竟可以为并发程序编写有效且自动化的测试用例,则可以解决其中的大部分问题。
CountDownLatch
@Test
public void should_publish_an_article_using_count_down_latch_to_fix() throws InterruptedException {
//Arrange
Article article = Article.newBuilder()
.withBody("learning how to test multithreaded java code")
.withId(1)
.withTitle("title").build();
CountDownLatch countDownLatch = new CountDownLatch(1);
when(this.articleRepository.findById(1)).thenReturn(article);
doAnswer(invocationOnMock -> {
System.out.println("Sending mail !!!");
countDownLatch.countDown();
return null;
}).when(this.emailSender).sendEmail(anyString(), anyString());
//Act
boolean publish = this.articlePublisher.publish(1);
//Assert
assertThat(publish).isTrue();
verify(this.articleRepository).findById(1);
countDownLatch.await();
verify(this.emailSender).sendEmail("Article Published With Id " + 1
, "Published an article with Article Title " + "title");
verifyNoMoreInteractions(this.articleRepository, this.emailSender);
}
我们使用CountDownLatch,以便主线程应等待,直到调用send email方法。我们调用countDownLatch的countDown方法进行暂停倒计时,直至awat()方法唤醒。
使用循环屏障Cyclic Barrier
@Test
public void should_publish_an_article_using_cyclic_barrier_to_fix() throws BrokenBarrierException, InterruptedException {
//Arrange
Article article = Article.newBuilder()
.withBody("learning how to test multithreaded java code")
.withId(1)
.withTitle("title").build();
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> System.out.println("Barrier opening"));
when(this.articleRepository.findById(1)).thenReturn(article);
doAnswer(invocationOnMock -> {
System.out.println("sending mail !!!");
cyclicBarrier.await();
return null;
}).when(this.emailSender).sendEmail(anyString(), anyString());
//Act
boolean publish = this.articlePublisher.publish(1);
//Assert
assertThat(publish).isTrue();
verify(this.articleRepository).findById(1);
cyclicBarrier.await();
verify(this.emailSender).sendEmail("Article Published With Id " + 1
, "Published an article with Article Title " + "title");
verifyNoMoreInteractions(this.articleRepository, this.emailSender);
}
循环屏障使这两个并发任务同步进行。当emailSender线程的sendEmail方法和主线程同步时,将打开屏障。
随着线程数量的增加,它们可能交错的方式也呈指数增长。根本不可能弄清楚所有这样的交错并对其进行测试。我们必须依靠工具为我们进行相同或相似的工作。幸运的是,其中有一些工具可以使我们的生活更轻松。