有没有办法测试方法处理并发调用的能力?



我有一个在数据库表中创建实体的方法。我想测试该方法同时被调用两次的情况。

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

有没有办法在不使测试片状的情况下对此进行测试?

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

测试测试。

获取应为线程安全的方法的副本,并使复制线程不安全。编写一个单元测试,该测试使用可运行的线程启动十几个线程:
1)具有共享的CountDownLatch。
2) 等待倒计时闩锁达到零。
3) 调用线程不安全方法。

使用 CountDownLatch,您可以触发所有线程或

多或少同时调用线程不安全方法(当 CountDownLatch 达到零时,所有线程都准备好从可运行中的同一点开始,但最终由您的操作系统(和硬件)决定何时执行什么)。评估结果:您现在应该看到差异(例如,插入了 11 条记录,而不是预期的 12 条记录)。

重复测试或将测试置于for循环中(并将CountDownLatch替换为CyclicBarrier,这是另一种可用于此类测试的并发工具)。无论如何,请确保您的测试始终显示不希望的结果(例如,永远不要假设线程同时启动,将它们与 CountDownLatch 等工具同步,以便您确切地知道线程的位置)。

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

我在这里没有给出太多细节,但问题中也没有太多细节。无论如何,一般的想法是首先设置一个应该出错的情况,并使用自动蛮力(许多线程在一个循环中多次调用该方法)结合智能使用并发工具(如 CountDownLatch)来显示测试将始终将某些问题浮出水面。"并发工具的智能使用"需要一些时间、洞察力和实践(例如,我花了一些时间才弄清楚,在调用"thread.start()"之后,线程可能还没有启动,并且可以使用 CountDownLatch 来确保线程在我想要的位置)。

免责声明:这些"经过测试"的测试不会捕获所有并发问题(您只测试您认为可能失败的内容,并且还有其他问题,例如损坏的双重检查锁定),但它们会强化您的代码并带来可能的并发问题浮出水面。

我最近遇到了类似的问题。以下是我为解决该特定问题而编写的确切代码:

  @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 个线程,我总是可以重现竞争条件或线程不安全引发的异常。

相关内容

最新更新