Java Concurrent Exception when using copy of lists [duplicate] - java

This question already has answers here:
Why am I not getting a java.util.ConcurrentModificationException in this example?
(10 answers)
Closed 7 years ago.
I have the following code in my class:
private static LinkedList<MyObject> myList =
new LinkedList<MyObject>();
public static void doEventStuff(String user, String event){
LinkedList<MyObject> copy;
synchronized (myList) {
copy = new LinkedList<>(myList);
}
for (MyObject o : copy) {
... do something with objects o
}
}
public static void removeObject(MyObject o) {
synchronized (myList) {
myList.remove(o);
}
o.doCleanup();
}
public static void terminate() {
synchronized (myList) {
for (MyObject o : myList) {
o.doCleanup();
}
myList.clear();
}
}
public static List<MyObject> getMyObjectsCopy() {
synchronized (myList) {
return new LinkedList<>(myList);
}
}
My problem is a ConcurrentModificationException when calling terminate() , specifically when iterating "for (MyObject o : myList) ".
The list myList is not passed around and can only be accessed through the static methods.
Also: the method MyObject.doCleanup() ca trigger events where the method "removeObject(MyObject)" can be called, when doing the iteration inside terminate() mthod , but since all the methods synchronize
on "myList", I didn't believe a concurrency exception can happen.
Can anyone help me with this issue?

This is not multi-threading issue per se, if you remove an object from the list in a foreach loop you will get ConcurrentModificationException.
And by the way, you can use CopyOnWriteArrayList instead

ConcurrentModificationException also happens if the list was modified while iterating over it using a 'foreach' loop. synchronize will help avoid other threads from accessing your list, but your issue is not due to thread-concurrency. If you want to delete (from the same thread) while iterating over the list, you must use an iterator and call iterator.remove().

In this code:
for (MyObject o : myList) {
o.doCleanup(o);
}
You call code, which internally calls removeObject() method. In this call we make myList.remove(o), which will change a list, as a result, it works like:
for (MyObject o : myList) {
myList.remove();
}
So, it's not a concurrency issue, it's just modification of collection in forEach loop over this collection. I think the best solution for this situation is to avoid removing from myList in doCleanup() code, it looks like lack of design.
Other possible solution - another doCleanup() method version which doesn't throw an event which cause removal from collection - you already do myList.clear().
Or rewrite removeObject() method like:
public static void removeObject(MyObject o) {
synchronized (myList) {
for (Iterator<MyObject> it = myList.iterator(); it.hasNext(); ) {
MyObject o1 = it.next();
if (o1.equals(o)) {
it.remove();
}
}
}
o.doCleanup();
}
like #geert3 recommends in his answer as far as I understand, but motivation in this answer is not fully clear for me.
But I don't like last solution - it looks like a hack for design problem because in this global collection maintaining code we call doCleanup() on deleted object which should call one more removeObject() inside event handler - I think it will be better to remove this "recursion".

Related

Is it thread safe to reassign a list in Java?

I'm playing with two threads operating on an ArrayList. I know you can't modify the list while iterating, but Java is letting me reassign (replace) the entire list in a loop with no issue. Is this threadsafe?
public class Main {
static List<String> list = Arrays.asList("hi", "there", "friend");
public static void main(String... args) throws InterruptedException {
boolean isReassigned = false;
for (String s: list) {
System.out.println(s);
if (!isReassigned) {
new Thread(() -> reassignList()).start();
isReassigned = true;
}
Thread.sleep(1000);
}
}
public static void reassignList() {
System.out.println("Reassigning list....");
list = Arrays.asList("goodbye", "old", "list");
}
}
I expected concurrency issues after "hi", but I got "hi there friend".
I assume this works because Java does copy-by-value on references, but I'm worried my production app that reassigns the array every 1 minute might blow up someday. Thanks everyone!
You are not seeing the updated list contents because the enhanced for loop uses an iterator behind the scenes. And hence, you are using the iterator (to the old list) to iterate over the data (even after the assignment is done). This means that both the lists are in-scope (and not Garbage collected). Once, isReassigned is true, if you get an iterator to the list you can see the new contents.
I've changed your code a bit (added an else block) to understand this
for (String s: list) {
System.out.println(s);
if (!isReassigned) {
new Thread(() -> reassignList()).start();
isReassigned = true;
} else {
System.out.println("Latest data " + list);
}
Thread.sleep(1000);
}
This produces
hi
Reassigning list....
there
Latest data [goodbye, old, list]
friend
Latest data [goodbye, old, list]
A few other points:
You should not be setting isReassigned from the main thread. It has to be set after you assign the list.
As #Kartik points out in the comments, there can be visibility issues and the other threads may not see the made changes. Marking the list and the isReassigned volatile will solve this.

Is it safe to loop over the same List simultaneously?

I have a list
testList= new ArrayList<String>(); // gets initialized once at the very beginning init(). This list never changes. It's static and final.
I have a static method that checks if an input value is in this List :
public static boolean isInList (String var1)
throws InterruptedException {
boolean in = false;
for (String s : testList) {
if (s.equals(var1))
{
in = true;
break;
}
}
return in;
}
I have a lot of threads that use this method concurrently and check if a certain value is in this list. It seems to work fine. However, I'm not sure if this is safe. Am I doing this correctly? Is it thread safe?
It is thread-safe as long as no thread is modifying the list while other threads are reading it.
If you are using iterators over the list, they will "fail-fast" (as in throw ConcurrentModificationException) if the list is modified under them. Other methods of accessing (i.e. get(n)) won't throw an exception but may return unexpected results.
This is all covered in great detail in the Javadoc for List and ArrayList, which you should study carefully.
ArrayList is not a thread safe object. It may works for you now, but in general, when working with threads, you should make sure you're using thread-safe objects that will work with your threads as you expect.
You can use Collections.synchronizedList()
testList = Collections.synchronizedList(new ArrayList<String>());
As long as you can guarantee that no one is writing to the list, it's safe.
Note that even if the list is static and final, the code itself doesn't guarantee that the list is never modified. I recommend using Collections.unmodifiableList() instead, because it guarantees that no element is ever added to or removed from the list.
By the way, you can rewrite your code to this:
public static boolean isInList(String var1) {
for (String s : testList) {
if (Objects.equals(s, var1)) {
return true;
}
}
return false;
}
or just
testList.contains(var1);

Java, adding elements in an array an concurrently looping through it

Sorry if this is a dumb question. But could someone explain me what could happens in a scenario like this?
List<Integer> scores = new Arraylist<>() ;
scores =
Collections.synchronizedList(scores)
public void add(int element) {
...
scores.add(element)
...
}
public String retrieve(int element) {
...
For (Integer e : scores)....
....
Return something
}
Let's assume that this class is a singelton and that scores is global. Multiple thread can add and retrieve the scores at the same time
In this scenario when starting the for loop and at the same time a thread is adding (or removing an element from the list) will it throw a concurrent modification exeption ?
Thank you
Bad things will happen, given the way you've written your example.
Your retrieve() method doesn't have its loop in a synchronized block, and both of your methods are accessing scores directly, instead of using the List returned by the Collections.synchronizedList() method.
If you take a look at the API for Collections.synchronizedList(), you'll notice that it says
In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list.
It is imperative that the user manually synchronize on the returned list when iterating over it:
Failure to follow this advice may result in non-deterministic behavior.
So you might get a ConcurrentModificationException, or something else weird might happen.
Edit
Even if all your access is via the synchronized List, you can still end up getting a ConcurrentModificationException thrown at you if you modify the List while iterating over it in another thread. That's why the Collections.synchronizedList() documentation insists that you manually wrap your iteration inside a block that is synchronized on the List it returns.
The API for ConcurrentModificationException says
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected. Iterators that do this are known as fail-fast iterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Your add method won't need to be changed, but your retrieve() method should look something like:
public String retrieve(int element) {
// stuff
synchronized (scores) { // prevent scores from being modified while iterating
for (Integer e : scores) {
// looping stuff
}
}
// more stuff
return something;
}
Sample Program
Here's a small sample program which demonstrates the behavior of safe vs unsafe access:
public class Scratch {
private List<Integer> scores = Collections.synchronizedList(new ArrayList<Integer>());
public static void main(String[] args) throws Exception {
final Scratch s = new Scratch();
s.scores.add(1);
s.scores.add(2);
s.scores.add(3);
// keep adding things to the list forever
new Thread(new Runnable() {
#Override
public void run() {
try {
int i=100;
while (true) {
Thread.sleep(100);
s.scores.add(i++);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
System.out.println("This will run fine");
s.safeLoop();
System.out.println("This will cause a ConcurrentModificationException");
s.unsafeLoop();
}
public void safeLoop() throws InterruptedException {
synchronized (scores) {
for (int i : scores) {
System.out.println("i="+i);
Thread.sleep(100);
}
}
}
public void unsafeLoop() throws InterruptedException {
for (int i : scores) {
System.out.println("i="+i);
Thread.sleep(100);
}
}
}

Implementation of singleton thread-safe list

I'm using Spring framework. Need to have a list of objects, which should get all data from database at once. When data is changed, list will be null and next get operation should fill data from database again. Is my code correct for multi-thread environment?
#Component
#Scope("singleton")
public class MyObjectHolder {
private volatile List<MyObject> objectList = null;
public List<MyObject> getObjectList() {
if (objectList == null) {
synchronized (objectList) {
if (objectList == null) {
objectList = getFromDB();
}
}
}
return objectList;
}
synchronized
public void clearObjectList() {
objectList = null;
}
}
Short answer: no.
public class MyObjectHolder {
private final List<MyObject> objectList = new List<>();
public List<MyObject> getObjectList() {
return objectList;
}
This is the preferred singleton pattern.
Now you need to figure out how to get the data into the list in a thread-safe way. For this Java already has some pre-made thread-safe lists in the concurrent package, which should be preferred to any synchronized implementation, as they are much faster under heavy threading.
Your problem could be solved like this:
public class MyObjectHolder {
private final CopyOnWriteArrayList<MyObject> objectList = new CopyOnWriteArrayList<>();
public List<MyObject> getObjectList() {
return objectList;
}
public boolean isEmtpy() {
return objectList.isEmpty();
}
public void readDB() {
final List<MyObject> dbList = getFromDB();
// ?? objectList.clear();
objectList.addAll(dbList);
}
}
Please note the absence of any synchronized, yet the thing is completely thread-safe. Java guarantees that the calls on that list are performed atomically. So I can call isEmpty() while someone else is filling up the list. I will only get a snapshot of a moment in time and can't tell what result I will get, but it will in all cases succeed without error.
The DB call is first written into a temporary list, therefore no threading issues can happen here. Then the addAll() will atomically move the content into the real list, again: all thread-safe.
The worst-case scenario is that Thread A is just about done writing the new data, while at the same time Thread B checks if the list contains any elements. Thread B will receive the information that the list is empty, yet a microsecond later it contains tons of data. You need to deal with this situation by either repeatedly polling or by using an observer pattern to notify the other threads.
No, your code is not thread safe. For example, you could assign objectList in one thread at time X, but set it to null (via clearObjectList()) at time X+1 because you are synchronizing on 2 different objects. The first synchronization is on objectList itself and the second synchronization is on the instance of MyObjectHolder. You should look into locks when using a shared resource instead of using synchonize, specifically something like a ReadWriteLock.

Is it a good way to prevent ConcurrentModificationException

The another question is about synchronized. I have also a run() in class Note,because i want to output each element in notes every 5 minutes. But i get always exception:java.util.ConcurrentModificationException,if i try to make more meetings in main. so i applay synchronized to the list notes which may be added a new meeting when i iterate over notes.My run method like this:
Is it correct way hier on list notes to synchronized to prevent ConcurrentModificationException ?(In my program it works.I get never this exception now)
A Meeting class and Note class may likes this:
public class Meeting{
public Meeting(Note note_1,Note note_2){
note_1.addElement(this);
note_2.addElement(this);}
//another method hier
}
public class Note implements Runnable{
public final List<Meeting> notes = new ArrayList<Meeting>();
public void addElement(Meeting n){
entries.add(n);
}
#Override
public void run(){
while(true) {
for(Meeting n : notes){
System.out.println(n.toString);}
}
try{ Thread.sleep(10);}
}
}
}
I get always exception error about exception:java.util.ConcurrentModificationException if i try to make more Meeting in main ,So i changes a littel in class Note,likes this :
private static final List<Entry> entries = Collections.synchronizedList(new ArrayList<Entry>());
and in run()
#Override
public void run() {
while(true){
synchronized(notes){
for(Entry n : entries){
//do something
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e ) {
}
}
}
}
From the javadoc
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 thow this exception.
THis means do not change your collection in a loop and iterate over it at the same time even in the same thread.
Read to what #Navi had written.
In a nutshell - NEVER remove/add elements of a collection in for each loop.
I once had that kind of problem and I decided to use http://code.google.com/p/google-collections/
There are some map/filter functions there (if I recall that methods were in Collections2 library).
If you are not willing to make the things right, you can always use the old-school iterator technique.
I've used a CopyOnWriteArrayList before when I encountered this sort of problem.
This makes a complete copy of the underlying array for each write, so it's not very efficient, but I've found it very useful for specific circumstances (e.g. a class which manages specialised event notification to some other classes)
This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException

Categories