JUnit并发访问synchronizedSet

时间:2016-10-03 14:28:32

标签: java junit concurrency synchronized

我在服务器上运行JUnit测试时遇到问题。当我在我的机器上运行测试时,完全没有问题。当我在服务器上运行它时,“有时”我的所有服务器都出现故障。这意味着测试有时会在60%的尝试中通过,40%会失败。

我正在使用Mockito。我的测试开始于使用vec.at(1).SetString("Modified string");模拟一些回复并将每个请求映射到响应,并且我正在使用vec.at(1)->SetString("Modified string");这是线程安全的。(我的synchronizedSet上的每个修改都发生在{{1然后,我使用MessageListener来获取特定REST端点的响应并声明一些值。

当测试失败并且我查看Stacktrace时,我看到我的一个映射(总是在同一个对象上)没有工作,并且在我的集合中这个特定请求和响应之间没有映射,当然,我获取Collections.synchronizedSet(new HashSet<>())请求此端点。

我正在使用Jenkins自动编译和运行测试,我得到失败的堆栈跟踪或我的Printlns,否则,没有可用的调试工具。

这听起来像是一个并发问题。我的意思是,似乎我的集合没有时间在synchronized(mySynchronizedSet){....}请求端点之前做好准备。我已经测试了锁,睡眠和另一个简单的java并发解决方案,但它们没有帮助,这个问题的概率特性使我陷入了死胡同。

每一个想法都会受到赞赏。

1 个答案:

答案 0 :(得分:2)

根据你所说的话,你似乎对3件具体案件的工作方式有误解。

第一

而且最明显的是,我为甚至提到这一点而道歉,但我之所以这样做的原因是因为我正在收集你还在学习(如果你还没有学习,我会道歉!并且同样的速度,你甚至可能没有用它的方式暗示它,所以如果我误读了对不起):你没有和Jenkins一起编译,你正在编译你机器上的任何JDK风格(无论是Oracle, Apple,GCJ等)。 Jenkins是一款自动化工具,可帮助您完成定期运行的繁琐工作。我只提到这个,因为我知道现在的大学生在开放课程中使用IDE,无法区分编译器,运行时和IDE。

其次

通过使用线程安全库,它不会自动使您所做的一切本身都是线程安全的。请考虑以下示例:

  final Map<Object, Object> foo = Collections.synchronizedMap(new HashMap <>());
  final String bar = "bar";
  foo.put(bar, new Object());
  new Thread(new Runnable(){
      @Override
      public void run(){
          foo.remove(bar);
      }
  }).start();
  new Thread(new Runnable(){
      @Override
      public void run(){
          if(foo.containsKey(bar)){
              foo.get(bar).toString();
          }
      }
  }).start();

无法保证第二个线程对#get(Object)的调用将在第一个线程调用#remove(Object)之前或之后发生。考虑一下

  1. 第二个主题可以调用#containsKey(Object)
  2. 然后第一个线程获得CPU时间并调用#remove(Object)
  3. 然后第二个线程现在有CPU时间并调用#get(Object)
  4. 此时,get(Object)的返回值将为null,对#toString()的调用将导致NullPointerDereference。你说你正在使用Set,所以这个使用Map的例子主要是为了证明一点:仅仅因为你使用的是线程安全集合,并不会自动使你做的所有线程都安全。我想你正在用你的集合做的事情与这种行为相匹配,但是没有代码片段,我只能推测。

    最后

    你应该小心编写JUnits的方法。正确的JUnit测试就是所谓的“白盒”测试。换句话说,您了解测试中发生的所有事情,并且您明确地测试了仅在被测单元中发生的所有事情。被测单元只是您调用的方法 - 您的方法调用的方法,只有方法本身。这意味着,您需要一个好的模拟框架,并模拟您的被测单元可能调用的任何后续方法调用。一些好的框架是JMockit,Mockito + PowerMock等。

    这一点的重要性在于您的测试应该测试您的孤立代码。如果您允许网络访问,磁盘访问等,那么您的测试可能会失败,并且可能与您编写的代码无关,并且它会完全使测试无效。在您的情况下,您提示网络访问,因此请假设您的交换机/路由器/等存在吞吐量问题,或者您的NIC缓冲区已满,并且无法快速处理您的程序尝试执行的操作。当然,失败并不好,应该修复,但应该在“黑盒”测试中进行测试。您的测试应该编写,以便消除这些问题,并且只在测试单元的特定方法中测试您的代码,而不是其他任何内容。

    编辑:我实际上发布了一个关于可能相关的白盒测试的单独讨论的答案:Is using a test entity manager a legitamate testing practice?