2016-01-28 61 views
1

我有一个方法可以在数据库表中创建一个实体。我想测试方法在同一时间被调用两次的场景。有没有测试方法处理并发呼叫的方法?

该方法有避免这种情况的逻辑,并且2个请求中的其中一个失败,但我无法确认这是否正常工作。

有没有办法在没有测试的情况下进行测试?

我粗暴地尝试从2个浏览器同时调用动作,但它们不是并行执行。我想这将需要大量的尝试和错误,让他们在同一时间执行(这是一个战争文件运行在tomcat上)

+0

为什么你不使用标准交易? – chrylis

+0

@chrylis您能否详细说明一下?你在谈论休眠事务吗? – Siddhartha

回答

1

我最近有一个类似的问题。这是确切的代码我写来解决特定的问题:

@Test 
    public void mapVertexToLazyObjectWithSameMapperOnMultipleThreadsAtTheSameTime() { 
    StackVertex vertex = new StackVertex(graph, 1L, "test", Optional.empty()); 
    mockVertex(vertex, NO_EDGES, NO_EDGES, properties("name", "foobar")); 

    VertexEntityMapper<NamedNode> mapper = objectMapper.getMapper(NamedNode.class); 
    NamedNode obj = mapper.mapToObject(vertex); 

    final Boolean[] flags = { false, false }; 
    Runnable run =() -> { 
     if (obj.getName() == null || !obj.getName().equals("foobar")) { 
     flags[0] = true; 
     } 
    }; 

    List<Thread> threads = IntStream.range(0, 10).boxed() 
     .map(i -> new Thread(run)).collect(toList()); 
    threads.forEach(Thread::start); 
    threads.forEach((thread) -> { 
     try { thread.join(); } catch (InterruptedException e) { 
     flags[1] = true; 
     } 
    }); 

    verify(vertexRepository, times(1)).findById(graph.tx(), 1L, Optional.empty()); 
    assertFalse("race condition",  flags[0]); 
    assertFalse("thread interrupted", flags[1]); 
    } 

这很简单,但完美的作品。如果我使代码线程不安全,这个测试很容易发现它的问题。我玩了一些线程,但发现有10个线程,我总是可以重现由线程不安全引发的竞态条件或异常。

+0

谢谢Lodewijk,这很酷!我会试一试。 – Siddhartha

+0

这似乎是一种不必要的详细说法,称为“从一堆并发线程中调用方法”,除非被测试的代码已知包含延迟(即使这样,它仍然不可靠),它是不可靠的。它“似乎有效”的事实并不意味着它是可靠的。它依赖于在所有线程启动并试图进行同时呼叫之前从未完成的代码。 – VGR

+0

@VGR我想我会给一次不同的答案。如果你喜欢,你可以用你的方式回答。此外,我在循环中测试了数百次;每次它会发现我的问题,如果我使代码线程不安全。这可能不是证明,但它对我来说已经足够了。 –

1

测试测试。

取一个应该是线程安全的方法的副本,并使副本线程不安全。编写一个单元测试,激发一个可运行的十几个线程:
1)共享CountDownLatch
2)等待CountDownLatch达到零。
3)调用线程不安全的方法。

通过CountDownLatch,您可以触发所有线程在大致相同的时间调用线程不安全方法(所有线程都准备好在CountDownLatch达到零时从可运行的同一点开始,但最终它是直到你的操作系统(和硬件)决定什么时候执行)。 评估结果:您现在应该看到一个差异(例如插入了11条记录,而不是您预期的12条记录)。

重复测试或将测试放在for-loop中(并用CyclicBarrier替代CountDownLatch,这是另一个可用于这类测试的并行工具)。在任何情况下,确保你的测试总是显示不希望的结果(例如,永远不要假设线程同时启动,将它们与CountDownLatch之类的工具同步,以便确切知道线程在哪里)。

通过调用单元测试中的线程安全方法,将调用替换为线程不安全的方法。您现在应该可以看到没有差异的预期结果。

我在这里没有提供太多的细节,但在问题中也没有太多细节。在任何情况下,总体思路是首先设置一个应该出错的情况,并使用自动化的暴力(许多线程在循环中多次调用该方法),并结合智能使用像CountDownLatch这样的并发工具来显示测试总是会带来某些问题。 “聪明使用并发工具”需要一些时间,洞察力和练习(例如,在调用“thread.start()”之后,我花了一些时间来弄清楚线程可能还没有启动,并且可以使用CountDownLatch以确保线程是我想要它的地方)。免责声明:这些“测试”测试不会捕获所有并发性问题(您只测试您认为可能会失败的情况,还有其他问题,例如破解Double checked locking),但它们会加强您的代码并带来可能性并发问题浮出水面。