Reputation: 15
I am trying to run 2 concurrent threads, where one keeps adding objects to a list and the other updates these objects and may remove some of these objects from the list as well.
I have a whole project where I've used ArrayList
for my methods and classes so it's difficult to change it now.
I've looked around and I found a few ways of doing this, but as I said it is difficult to change from ArrayList
. I tried using synchronized
and notify()
for the method adding the objects to the list and wait()
for the method changing these objects and potentially removing them if they meet certain criteria.
Now, I've figured out how to do this using a CopyOnWriteArrayList
, but I would like to know if there's a possibility of using ArrayList
itself to simulate this. so that I don't have to edit my entire code.
So, basically, I would like to do something like this, but with ArrayList
:
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class ListExample{
CopyOnWriteArrayList<MyObject> syncList;
public ListExample(){
syncList = new CopyOnWriteArrayList<MyObject>();
Thread thread1 = new Thread(){
public void run(){
synchronized (syncList){
for(int i = 0; i < 10; i++){
syncList.add(new MyObject(i));
}
}
}
};
Thread thread2 = new Thread(){
public void run(){
synchronized (syncList){
Iterator<MyObject> iterator = syncList.iterator();
while(iterator.hasNext()){
MyObject temp = iterator.next();
//this is just a sample list manipulation
if (temp.getID() > 3)
syncList.remove(temp);
System.out.println("Object ID: " + temp.getID() + " AND list size: " + syncList.size());
}
}
}
};
thread1.start();
thread2.start();
}
public static void main(String[] args){
new ListExample();
}
}
class MyObject{
private int ID;
public MyObject(int ID){
this.ID = ID;
}
public int getID(){
return ID;
}
public void setID(int ID){
this.ID = ID;
}
}
I've also read about Collections.synchronizedList(new ArrayList())
but again, I believe this would require me to change my code as I have a substantial number of methods that take ArrayList
as a parameter.
Any guidance would be appreciated, because I am out of ideas. Thank you.
Upvotes: 1
Views: 6552
Reputation: 7854
Of course you should be using java.util.concurrent
pakage. But let's look at what is happening/could happen with only ArrayList
and synchronization.
In your code, if you have just ArrayList
in place of CopyOnWriteArrayList
, it should work as you have provided full synchronization synchronized (syncList)
on whatever you are doing/manipulating in threads. You do not require any wait()
notify()
if whole thing is synchronized (But that's not recommended, will come to that).
But this code will give ConcurrentModificationException
because once you are using iterator syncList.iterator()
you should not remove element from that list, otherwise it may give undesirable results while iterating that's why it's designed to fail fast and give exception. To avoid this you can use like:
Iterator<MyObject> iterator = syncList.iterator();
ArrayList<MyObject> toBeRemoved = new ArrayList<MyObject>();
while(iterator.hasNext()){
MyObject temp = iterator.next();
//this is just a sample list manipulation
if (temp.getID() > 3)
{
//syncList.remove(temp);
toBeRemoved.add(temp);
}
System.out.println("Object ID: " + temp.getID() + " AND list size: " + syncList.size());
}
syncList.removeAll(toBeRemoved);
Now regarding synchronization, you should strive to minimize its scope otherwise there'll be unnecessary waiting between threads, thats why java.util.concurrent package is given to have high performance in multithreading (using even non blocking algorithms). Or you can also use Collections.synchronizedList(new ArrayList())
but they are not as good as concurrent
classes.
If you want to use conditional synchronization like in producer/consumer problem, then you can use wait()
notify()
mechanism on same object (lock). But again there're already some classes to help like using java.util.concurrent.LinkedBlockingQueue
.
Upvotes: 1
Reputation: 1988
Use Vector
instead of ArrayList
. Remember to store it in a List
reference as Vector contains deprecated methods. Vector, unlike ArrayList, synchronizes its internal operations, and unlike CopyOnWriteArrayList
, does not copy the internal array each time a modification is made.
Upvotes: 1
Reputation: 6622
As a quick solution u may extend ArrayList and make modifying methods (add/remove) synchronized. and re-factor code to replace ArrayList to your custom-ArrayList
Upvotes: 1
Reputation: 12398
You may be interested on the collections provided by the java.util.concurrent
package. They are very useful for producer/consumer scenarios, where one or more threads add things to a queue, and other threads take them. There are different methods depending on whether you want to block, or fail when the queue is full/empty.
About refactoring your methods, you should have used interfaces (e.g. List
) instead of concrete implementation classes (such as ArrayList
). That is the purpose of interfaces, and the Java API has a good suply of them.
Upvotes: 5