0

I am running my application for load testing using JMeter and getting ConcurrentModificationException in a scenario where (I think) its not supposed to come. This occurred only for few cases and not all.

I am having an ArrayList which is a class variable in which elements are added through a method. Later on in a different method which is invoked after the list is prepared, iteration is going on using stream and a local arraylist is getting prepared using some filters as follows

...
//class variable getting elements from a separate method
List<String> myGlobalList = new ArrayList(); 
...

public String returnValue(String _value){
  List myLocalList = new ArrayList();
  myGlobalList.stream().filter(ele -> {
    ele = ele.replaceAll("\\[(.*?)\\]", "$1");

    String[] strArr = ele.split(",");
    for(String sArr : strArr) {
      if(sArr.contains(_value)){
        System.out.println("sArr ---- > "+sArr);
        myLocalList.add(sArr);
        return true ;
      }
    }
    return false;
  }).findFirst();

  return myLocalList.size()>0 ? myLocalList.get(0): null;
}

The findFirst() method is throwing ConcurrentModificationException only under load (not for every request).

Can't figure out why it's happening. Am I missing any key? How can this code be modified to avoid this exception in all cases?

Update:

Below is a snippet from the stacktrace for the exception

java.util.ConcurrentModificationException
at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1353)
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
at com.pkg.MyClass.returnValue(MyClass.java:501)
qwerty
  • 2,392
  • 3
  • 30
  • 55
  • 1
    @HadiJ that is modifying an element, but not changing the List structurally. – Eugene Apr 17 '18 at 10:55
  • @HadiJ Is there the issue? No modification (add/remove) to the collection is being done there... If that's the case...it's totally weired. Can you pls give some insight? – qwerty Apr 17 '18 at 10:55
  • @Eran No threading involved. Its only the huge amount of requests which are coming to the server. – qwerty Apr 17 '18 at 10:56
  • @Eugene exactly – qwerty Apr 17 '18 at 10:57
  • post the stack trace you get also – Eugene Apr 17 '18 at 10:58
  • @Eugene yea sure – qwerty Apr 17 '18 at 11:06
  • So there is threading involved, each request is a thread. – M. Deinum Apr 17 '18 at 11:15
  • @OleV.V. yes but the localList. Iteration is on global list – qwerty Apr 17 '18 at 11:22
  • @M.Deinum But for each thread a new arraylist object is created. – qwerty Apr 17 '18 at 11:23
  • 2
    Your entire method can be replaced by `return myGlobalList.stream().flatMap(ele -> Arrays.stream(ele.replaceAll("\\[(.*?)\\]", "$1").split(","))) .filter(sArr -> sArr.contains(_value)) .findFirst() .orElse(null);`, but of course, that’s not your issue. The list has been modified and since it isn’t this code, it’s some other code modifying your list. But we can’t tell you anything about the code you haven’t posted. – Holger Apr 17 '18 at 11:28
  • avoid this type of Exception when you used ConcurrentCollection. @qwerty – Ng Sharma Apr 17 '18 at 11:29
  • @Holger Thanks for the suggestion :) AFA the global list is concerned, no code is modifying it once addition of elements to it is over. Its just a sequence of method invocations. Also, for async requests, the global list is not shared among the requests and program is not running in a multithreaded environment. – qwerty Apr 17 '18 at 11:36
  • Without seeing your code in full it isn't feasible to give you an answer. The error clearly indicates you are sharing state between threads (although you might think you aren't). Request handling threads are shared. – M. Deinum Apr 17 '18 at 11:57

1 Answers1

0

Don't you need to get the first String in list? why not get it without using List:

  public String returnValue(String _value){   
  String findFirst = myGlobalList.stream().filter(ele -> {
    ele = ele.replaceAll("\\[(.*?)\\]", "$1");

    String[] strArr = ele.split(",");
    for(String sArr : strArr) {
      if(sArr.contains(_value)){
        System.out.println("sArr ---- > "+sArr);
        //myLocalList.add(sArr);
        return true ;
      }
    }
    return false;
  }).findFirst().get();

  return findFirst;
}

EDIT

With Optional check:

        Optional<String> findFirst = myGlobalList.stream().filter(ele -> {
        ele = ele.replaceAll("\\[(.*?)\\]", "$1");

        String[] strArr = ele.split(",");
        for (String sArr : strArr) {
            if (sArr.contains(_value)) {
                System.out.println("sArr ---- > " + sArr);
                // myLocalList.add(sArr);
                return true;
            }
        }
        return false;
    }).findFirst();
    return findFirst.isPresent() ? findFirst.get() : null;

EDIT 2

findFirst is throwing the exception, because original list have been changed, try collecting your results to new list using collect and then get the first value:

 return myGlobalList.stream().filter(ele -> {
        ele = ele.replaceAll("\\[(.*?)\\]", "$1");

        String[] strArr = ele.split(",");
        for (String sArr : strArr) {
            if (sArr.contains(_value)) {
                //System.out.println("sArr ---- > " + sArr);
                // myLocalList.add(sArr);
                return true;
            }
        }
        return false;
    }).collect(Collectors.toList()).get(0);
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
  • Good point. But the exception is thrown for the list under iteration and not for the list where element is getting added. The *for* loop itself is not getting executed in this case. – qwerty Apr 17 '18 at 11:21
  • @qwerty Did you tried the solution? also can you add the code that you insert data into myGlobalList? – Ori Marko Apr 17 '18 at 11:25
  • 1
    Instead of `findFirst.isPresent() ? findFirst.get() : null` you should use `findFirst.orElse(null)`, but anyway, your code isn’t doing the same as the OP’s. – Holger Apr 17 '18 at 11:31
  • @qwerty last check, can you remove `System.out.println` line?, see https://stackoverflow.com/questions/19616864/why-concurrentmodificationexception-when-print-a-set-with-system-out-println – Ori Marko Apr 17 '18 at 11:38
  • Oh nice. But these prints were not there earlier...when this exception was coming. These were just to check the values involved in the whole process. – qwerty Apr 17 '18 at 11:40
  • @qwerty updated solution, with collecting results to a new list – Ori Marko Apr 17 '18 at 12:17