2013-02-21 37 views
2

我试图写一个集成测试,导致一个InterruptedException从生产代码提出:之前调用的Future.get()中断线程

@Test 
public void test() { 
    productionObject = new ProductionObject(
      com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor()); 
    Thread.currentThread().interrupt(); 
    assertThat(productionObject.execute(), equalTo(defaultResponse)); 
} 

productionObject的实现:

try { 
    for (Future<T> future : executorService.invokeAll(tasks))) { 
     results.add(future.get()); 
    } 
    return results; 
} catch (InterruptedException e) { 
    Thread.currentThread().interrupt(); // preserve interrupt flag 
    return defaultResponse; 
} 

里面AbstractQueuedSynchronizer.acquireSharedInterruptibly()我看到:

if (Thread.interrupted()) 
     throw new InterruptedException(); 

所以我期望这个测试通过一致。

我在构建服务器中看到过这个失败(返回的是results而不是defaultResponse)。我一直无法在本地重现失败,在一段时间(true)循环中运行测试,并通过软件渲染运行glxgears来模拟较高的负载;-)任何人都可以发现我的错误,给我一些关于去哪里寻找的建议,或建议可以帮助我的工具?

+0

构建服务器是否使用不同的JVM?也许它的executorService工作方式不同。 – Kenster 2013-02-21 16:13:13

+0

@hertzsprung是否有任何情况下'results.isEmpty()'在构建服务器中返回true? – 2013-02-21 16:40:05

+0

我试图重现问题,但没有成功。您是否提供了ProductionObject的所有相关代码?我在问,因为也许代码中的某处中断标志正在重置。这可能是通过对Thread.interrupted()的无辜调用,或者通过处理InterruptedException而不再次设置标志来实现的。您是否尝试过调试以找到标志变为假的地方? – 2013-02-21 21:18:37

回答

0

我已经通过中断Callable而不是来自测试方法本身的线程来“修复”这个问题。这使得中断发生的更接近acquireSharedInterruptibly()

我只能想象在代码路径的某个地方有时会清除中断标志(可能由JUnit或Maven surefire,它们并行地执行测试方法)。我可能只是减少了竞争条件的可能性,而不是修复它: -/

1

奇怪。我以同样的方式阅读代码。我看到:

  1. FutureTask.get()来电Sync.get()。我假设我们在这里处理FutureTask
  2. Sync.get()调用Sync.innerGet()
  3. Sync.innerGet()电话acquireSharedInterruptibly(0);
  4. 其中有正确的代码关:

    if (Thread.interrupted()) 
        throw new InterruptedException(); 
    

我认为,这将总是抛出。也许有某种竞争条件,所以线程还不知道它已被打断?中断线程后,你有没有试过睡100ms?

我刚才在我的multi-cpu的Mac上运行了下面的测试,它从不失败,所以它看起来不像竞争条件 - 至少在我的架构和JRE版本1.6.0_41中。

for (long i = 0; i < 10000000; i++) { 
    Thread.currentThread().interrupt(); 
    assertTrue(Thread.interrupted()); 
} 
+0

我们正在处理番石榴的'ListenableFutureTask',它扩展了'FutureTask'(并且不覆盖'Future.get()')。如果我正在使用'SameThreadExecutor',睡觉肯定没有意义吗?也许使用'SameThreadExecutor'不是最好的想法,因为测试并不反映生产代码中使用的ExecutorService实现。 – hertzsprung 2013-02-21 16:21:49

+0

在你的中断调用@hertzsprung之后添加一个'assertTrue(Thread.currentThread()。isInterrupted()))'会很有趣。 – Gray 2013-02-21 16:25:50

+0

invokeAll()呢?我预计甚至在future.get()调用之前,该行将被中断。这个方法在这个特定的ExecutorService中如何实现? – 2013-02-21 16:36:33

0

在这种情况下使用sameThreadExecutor()实际上可能是禁忌,因为生产中断可能比发生在的任务之一。否则代码看起来很好。尝试使用踢掉其他线程,并让其中一个任务等待足够长的时间以等待中断。

相关问题