ArrayList.addAll() ConcurrentModificationException - java

I'm facing some really strange problems while implementing a kind of Kademlia bucket in Java 8 (OpenJDK).
I need to get at least a specific number of items from so-called Buckets.
But that's not the problem.
Somehow, I get sometimes a ConcurrentModificationException while doing closest.addAll() on the ArrayList though it is just used in a single thread and I'm not iterating or doing something like that.
Do you know how to help me?
Here is my code (I know it's a mess!):
List<Neighbour> getClosest(Node n, int num) {
ArrayList<Neighbour> closest = new ArrayList<>();
int missing;
int walkDown = n.getBucket(me);
int walkUp = walkDown + 1;
boolean pleaseBreak = true;
while (true) {
missing = num - closest.size();
if (missing <= 0) {
return closest;
}
if (walkUp >= 0 && walkUp < 160) {
List<Neighbour> l = buckets[walkUp].getClosest(missing);
closest.addAll(l);
if (closest.size() >= missing) {
return closest;
}
walkUp++;
pleaseBreak = false;
}
if (walkDown >= 0 && walkDown < 160) {
List<Neighbour> l = buckets[walkDown].getClosest(missing);
closest.addAll(l);
if (closest.size() >= missing) {
return closest;
}
walkDown--;
pleaseBreak = false;
}
if (pleaseBreak) {
return closest;
}
pleaseBreak = true;
}
}

ConcurrentModificationException actually means that you are breaking the rules of iteration by somehow modifying the list while iterating it.
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
That said it is fairly clear what could be causing this issue. As closest is a new List being filled by the method it must be l that is being modified.
There are two options:
Another thread is doing it.
You already have an iterator open across the list l.
Assuming it is not 1 (or you would probably have mentioned that) I will go for:
Your getClosest method is returning a sublist of a list that is being iterated across and/or modified and addAll is also attempting to iterate over it.
To fix, make getClosest return a copy of the sublist.

Related

How does my comparator method violate its general contract?

Third-Party Packages
import java.util.Collections;
import java.util.Comparator;
import org.joda.time.DateTime;
My Comparator
public static Comparator<Task> TASK_PRIORITY = new Comparator<Task>() {
public int compare(Task task1, Task task2) {
if (task1 == null && task2 == null) return 0;
if (task1 == null) return +1; //null last
if (task2 == null) return -1; //null last
// Only consider retries after a task is retried 5+ times
if (task1.getRetries() >= 5 || task2.getRetries() >= 5) {
// Primary sort: retry count (ascending)
int retriesCompare = Integer.compare(task1.getRetries(), task2.getRetries());
if (retriesCompare != 0) return retriesCompare;
}
// Secondary sort: creation time (ascending, null first)
int creationCompare = compareTimeNullFirst(task1.getCreationTime(), task2.getCreationTime());
if (creationCompare != 0) return creationCompare;
// Tertiary sort: load time (ascending, null last)
int loadCompare = compareTimeNullLast(task1.getLoadTime(), task2.getLoadTime());
if (loadCompare != 0) return loadCompare;
return 0;
}
};
private static int compareTimeNullLast(DateTime time1, DateTime time2) {
if (time1 == null && time2 == null) return 0;
if (time1 == null) return +1;
if (time2 == null) return -1;
if (time1.isBefore(time2) return -1;
if (time1.isAfter(time2)) return +1;
return 0;
}
private static int compareTimeNullFirst(DateTime time1, DateTime time2) {
if (time1 == null && time2 == null) return 0;
if (time1 == null) return -1;
if (time2 == null) return +1;
if (time1.isBefore(time2) return -1;
if (time1.isAfter(time2)) return +1;
return 0;
}
Using My Comparator
//tasks is a List<Task>
Collections.sort(tasks, TASK_PRIORITY);
My Problem
I sometimes get an IllegalArgumentException for Comparison method violates its general contract!. I can consistently get this Exception thrown with live data running long enough, but I'm not sure how to fix the actual cause of the problem.
My Question
What is wrong with my comparator? (Specifically, which part of the contract am I violating?) How do I fix it without covering up the Exception?
Notes
I'm using Java 7 and cannot upgrade without a major rewrite.
I could possibly cover-up the Exception by setting java.util.Arrays.useLegacyMergeSort to true, but that is not a desirable solution.
I tried to create tests to randomly generate data and verify each of the contract conditions. I wasn't able to get the exception to be thrown.
I tried removing the condition around the retry comparison, but I still eventually got the Exception.
This line throws the exception: Collections.sort(tasks, TASK_PRIORITY);
Let us start at the beginning. My reading of your code is that the logic of your Comparator is sound. (I would have avoided having null Task and DateTime values, but that is not relevant to your problem.)
The other thing that can cause this exception is if the compare method is giving inconsistent results because the Task objects are changing. Indeed, it looks like it is semantically meaningful for (at least) the retry counts to change. If there is another thread that is changing Task the fields that can effect the ordering ... while the current thread is sorting ... that could the IllegalArgumentException.
(Part of the comparison contract is that pairwise ordering does not change while you are sorting the collection.)
You then say this:
I use ImmutableSet.copyOf to copy the list before sorting, and I do that under a read lock in java.util.concurrent.locks.ReadWriteLock.
Copying a collection doesn't make copies of the elements of the collection. It is a shallow copy. So you will end up with two collections that contain the same objects. If the other thread mutates any of the objects (e.g. by increasing retry counts), that could change the ordering of objects.
The locking makes sure that you have consistent copy, but that's not what the problem is.
What is the solution? I can think of a couple:
You could lock something to block all updates to the collections AND the element objects while you copy and sort.
You could deep-copy the collect; i.e. create a new collection containing copies of the elements of the original collection.
You could create light-weight objects that contain a snapshots of the fields of the Task objects that are relevant to sorting; e.g.
public class Key implements Comparable<Key> {
private int retries;
private DateTime creation;
private DateTime load;
private Task task;
public Key(Task task) {
this.task = task;
this.retries = task.getRetryCount();
...
}
public int compareTo(Key other) {
// compare using retries, creation, load
}
}
This has the potential advantages that you are copying less information, and you can go from the sorted collection of Key objects to the original Task objects.
Note all of these alternatives are slower than what you are currently doing. I don't think there is a way to avoid this.

Is it safe clear a Set in a loop if it finds the correct value?

I'm in this situation: if I find a specific value in a HashSet, I have to update a field, clear the set and return the field.
Here one example:
static Set<Integer> testSet = new HashSet<>();
static Integer myField = null; // the field could be already != null
public static int testClearSet()
{
for (int i = 0; i < 100; i++) { // this is just for the test
testSet.add(i);
}
for (Integer n : testSet) {
if (n == 50) {
myField = n;
testSet.clear();
return myField;
}
}
return -1;
}
I'm wondering if doing this to the set it's safe, considering the fact that later on I should reuse the set.
I'm asking this, because I knew that to make changes over a Collection while iterating, is not a "good practice", but this case I think is a little bit different.
A possible solution would be:
boolean clear = false;
for (Integer n : testSet) {
if (n == 50) {
myField = n;
clear = true;
break;
}
}
if (clear) {
testSet.clear();
return myField;
}
So, which one is the right way?
It should be safe to remove elements from a set when using an explicit iterator. Hence the following should be safe:
Iterator<Integer> iterator = testSet.iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
if (element.intValue() == 50) {
testSet.clear();
break;
}
}
A ConcurrentModificationException is only thrown if you continue iterating after changing it manually.
What you do is change it and abort iterating, so it should be 100% safe (regardless of the for-each implementation).
The real issue is, the readability of the code. A piece of code should ideally do one job, and if this job is complicated, split it up. In particular, your code has two parts, a condition and an action:
if (some condition) do some action
So:
public static int testClearSet() {
if (setConatins(50)) {
myField = 50;
testSet.clear();
return myField;
}
return -1;
}
private static boolean setConatins(int searchFor) {
for (Integer n : testSet) {
if (n == searchFor) {
return true;
}
}
return false;
}
The latter method can be replaced with a single API call, for you to figure out.
If you know that your Set changing only in one thread, so you can clean it like in first example.
Method clear() does not throw ConcurrentModificationException.
Both your code will work.
There is indeed a restriction in modifying the collection when u iterate using fail fast iterators. That means, iterating using fail fast iterator will fail if there is any modification in the collection after the iterator was created. All the default iterators that is returned by java collection classes are fail-fast iterators.
private void removeDataTest (Collection<String> c, String item) {
Iterator<String> iter = c.iterator(); //Iterator is created.
while (iter.hasNext()) {
String data = iter.next();
if (data.equals(item)) {
//c.remove(data); //Problem. Directly modifying collection after this iterator is created. In the next iteration it will throw concurrent modification exception.
iter.remove(); //This is fine. Modify collection through iterator.
//c.clear(); break; //This is also should be okay. Modifying the collection directly, but after that it is breaking out and not using the iterator.
}
}
}
In your code, u don't continue iteration after the set is modified. So should be fine.

Recursive implementation of a method

I'm new to Java and still trying to wrap my head around recursion.The function below returns true at the very first intersection between the two sorted lists list x and list y.
public static boolean checkIntersection(List<Integer> x, List<Integer> y) {
int i = 0;
int j = 0;
while (i < x.size() && j < y.size()) {
if (x.get(i).equals(y.get(j))) {
return true;
} else if (x.get(i) < y.get(j)) {
i++;
} else {
j++;
}
}
return false;
}
Now I've been trying to implement it using recursion instead, and I know that there should be a base case which is an empty list in this case and then try to reduce the list by excluding one element at a time and feed it back to the same recursive function, but I can't work out how to check for intersection as I pass the rest of the list over and over.
public static boolean recursiveChecking(List<Integer> x,List<Integer> y) {
if(x.size() == 0){
return false;
}
else {
return recursiveChecking(x.subList(1, x.size()-1), y);
}
}
Any help would be highly appreciated. Thank you.
General approach to making something recursive is to think of two things:
When can I produce an answer trivially? - An answer to this question lets you code the base case. In your situation, you can produce the answer trivially when at least one of two lists is empty (the result would be false) or the initial elements of both non-empty lists are the same (the result would be true)
How do I reduce the problem when the answer is non-trivial? - An answer to this question lets you decide how to make your recursive call. In your case you could, for example, remove the initial element of one of the lists before making the recursive call*, or pass ListIterator<Integer> in place of List<Integer> for a non-destructive solution.
*Of course in this case you need to take care of either adding your numbers back after the call, or make a copy of two lists before starting the recursive chain.
As the lists are ordered, your recursion should remove the first element of the list with the smaller first value. Then you have to return true, if both lists start with the same number and false if any of the lists is empty. Otherwise you keep removing elements. This would look something like this (This code is untested):
public static boolean recursiveChecking(List<Integer> x,List<Integer> y) {
if(x.size() == 0 || y.size() == 0){
return false;
} else if (x.get(0).equals(y.get(0))) {
return true;
} else {
if (x.get(0) < y.get(0)) {
return recursiveChecking(x.subList(1, x.size()-1), y);
} else {
return recursiveChecking(x, y.subList(1, y.size()-1));
}
}
}

Java ConcurrentModificationException when removing two indexes, current and random one

I have a list of players.
This list contains players which do not have targets. Targets means that a player is targeted to another player, they two are targets, and should not be in the list.
The purpose of the following loop is to loop through all players, and search for a target, and if the player is not ready yet, it will just call the tick() method, which basically ticks down the target search timer. isReady method is basically timer == 0
for (Client c : participants) {
PlayerTargetDomain dom = c.getTarget();
if (dom.isReady()) {
if (dom.getSearchDelay() == 0) {
SharedTargetDomain d;
if ((d = search(c)) != null) {
participants.removeAll(Arrays.asList(d.getFirst(), d.getSecond()));
continue;
}
}
else {
dom.tickSearchDelay();
}
}
else dom.tick();
}
Now the search() method, basically looks for a matching target, and if found it will build SharedTargetDomain which contains the current index, and the found target index objects.
If the returned SharedTargetDomain instance from search(Client) was not null, I will remove both of the objects from the participants list using removeAll()
Unfortunately, if I remove any of them I will get the following error:
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at mod.game.TargetManager.execute(TargetManager.java:24)
The line 24 is this:
for (Client c : participants) {
Why am I getting this? I've tried using Iterator for the current index, but I still get the error because I am deleting the other index too, but what does the other one has to do with the current index if I delete it? I am really misunderstanding something.
Thanks!
Iterator implementation:
Iterator<Client> itr = participants.iterator();
while(itr.hasNext()) {
Client c = itr.next();
if (c != null) {
PlayerTargetDomain dom = c.getTarget();
if (dom.isReady()) {
if (dom.getSearchDelay() == 0) {
SharedTargetDomain d;
if ((d = search(c)) != null) {
participants.remove(d.getSecond());
itr.remove();
continue;
}
}
else {
dom.tickSearchDelay();
}
}
else dom.tick();
}
}
Problem is that you modifying collection while iterating it.
There are at least two solution
Use index access. You get a lot of fun handling indexes because element will be shifted after remove.
Collect elements that you need to remove and apply all changes after finish iteration, You get a lot of fun always having in mind that element you processing can be already scheduled for deletion.
You can use
CopyOnWriteArrayList to avoid this exception

Solving a ConcurrentModificationException

I am writing a little game which has many circles moving on the screen.
I am managing the circles in two threads as following:
public void run() {
int stepCount = 0;
int dx;
int dy;
while (m_threadTrap){
dx = 0;
dy = 0;
synchronized (m_circles) {
for (Iterator<Circle> it = m_circles.iterator(); it.hasNext();){
Circle c = it.next(); //Exception thrown here.
if (c.getDirX() != 0)
if (stepCount % c.getDirX() == 0){
dx = 1;
}
if (c.getDirY() != 0)
if (stepCount % c.getDirY() == 0){
dy = 1;
}
c.move(dx, dy);
}
}
if (stepCount == 150000){
stepCount = 0;
}
stepCount++;
}
}
m_circles in an ArrayList of Circles.
And the following Thread:
public void run() {
while (m_threadTrap){
int topPosition;
int bottomPosition;
int leftPosition;
int rightPosition;
ArrayList<Circle> removedCircles = new ArrayList<Circle>();
synchronized (m_circles.getCircles()) {
for (Iterator<Circle> it = m_circles.getCircles().iterator(); it.hasNext();){
Circle c = it.next();
// Some calculation to evaluate which circles should be removed
removedCircles.add(c);
}
}
}
try{
Thread.sleep(25);
}
catch (Exception e) { }
m_circles.getCircles().removeAll(removedCircles);
if (m_circles.getCircles().size() < 30)
m_circles.addNewCircle();
repaint();
}
}
My problem is that I get ConcurrentModificationException at the line
Circle c = it.next();
in the first thread. At first I tried going over the ArrayList with a foreach loop and this gave me the same exception.
After researching a bit on this exception I saw two solutions:
1. Putting the part which accesses the collection in a synchronized block.
2. Using the Iterator object of the collection.
Neither of them solved it for me.
For a synchronized() {} block to be effective, all accesses to the protected objects must be wrapped in synchronized blocks. You probably forgot to wrap some access.
Another "gotcha" is that ConcurrentModificationException can also mean that it was concurrently modified in the same thread. For example, if you remove an element from a collection while traversing it, you may get this exception. (As an exception, you can safely remove elements via the iterator itself)
ConcurrentModificationException means that you are iterating over a collection and, while iterating, someone (the current thread or another one) modified the underlying collection without using Iterator.remove(). Whenever you invoke an operation on the Iterator it checks that the underlying collection has not been changed. Using a foreach does not change a thing, since it uses an Iterator to perform the loop.
Your solution are:
create a new collection:
for (Circle c: new ArrayList(m_circles.getCircles()).iterator()) {
// Some calculation to evaluate which circles should be removed
removedCircles.add(c);
}
or synchronize both threads on the same object (you synchronize on different objects, therefore it does not do anything) whenever you are modifying or accessing the collection

Categories

Resources